読者です 読者をやめる 読者になる 読者になる

Perl日記

PerlとかRubyとかPHPとかPythonとか

looks_like_number()返却値の謎

前々回前回のエントリに引き続き、looks_like_number()の返却値を調べてみた。

use Scalar::Util qw/ looks_like_number /;
say looks_like_number(123); #=> 4352

1が出ると思っていたのだけれど。


もう一度perldocしてみる。
The CPAN Search Site - search.cpan.org

perldoc Scalar::Util

looks_like_number EXPR
   Returns true if perl thinks EXPR is a number. See "looks_like_number"
   in perlapi.

うわお。perlapi見てくれとな。
見てなかった。
perlapi - perldoc.perl.org

perldoc perlapi

looks_like_number
   Test if the content of an SV looks like a number (or is a number).
   "Inf" and "Infinity" are treated as numbers (so will not issue a
   non-numeric warning), even if your atof() doesn’t grok them.

   I32     looks_like_number(SV *const sv)

looks_like_number
 ScalarValueの中身が数値に見えるかどうか(あるいは数値であるかどうか)をテストします。
 "Inf"と"Infinity"は数値として扱われます(なので"数値でない"という警告は生じません)。
 たとえatof()がそれらを把握しなくても。

で、スカラ変数一個引数にとって、32bit整数値が返却されます、ということか。

なるほどなるほど。
ていうかめちゃくちゃXSじゃん……。


というわけで改めてコードをちゃんと見てみる。

/path/Scalar/Util.pm

package Scalar::Util;
# (略)
require List::Util; # List::Util loads the XS
# (略)
unless (defined &dualvar) {
  # Load Pure Perl version if XS not loaded
  require Scalar::Util::PP;
  Scalar::Util::PP->import;
  push @EXPORT_FAIL, qw(weaken isweak dualvar isvstring set_prototype);
}

あ、List::Utilからとってきてたのか。

/path/List/Util.pm

# (略)
    require XSLoader;
    XSLoader::load('List::Util', $XS_VERSION);

Scalar::Utilの上位にList::Utilがあったのか。
つまり、unless の中通るのは、

  • Scalar::UtilがList::Utilをrequire
  • List::UtilがXS読み込み
  • XS読み込み失敗したらサブルーチンdualvar()がインポートできない
  • Scalar::UtilがPurePerl(PP)を用いたバージョンのpackageをインポートしてカバー

という流れか。
言ってみれば今まで僕が見ていたPerlバージョンのlooks_like_number()は保険だったのだ。
そりゃ挙動が違うよなー。
ちょっと適当に見すぎていたよ。


ではXSインポート成功時に使われる本物のlooks_like_number()のコードはというと。

ListUtil.xs

int
looks_like_number(sv)
        SV *sv
PROTOTYPE: $
CODE:
  SV *tempsv;
  if (SvAMAGIC(sv) && (tempsv = AMG_CALLun(sv, numer))) {
    sv = tempsv;
  }
  else if (SvMAGICAL(sv)) {
      SvGETMAGIC(sv);
  }
#if (PERL_VERSION < 8) || (PERL_VERSION == 8 && PERL_SUBVERSION <5)
  if (SvPOK(sv) || SvPOKp(sv)) {
    RETVAL = looks_like_number(sv);
  }
  else {
    RETVAL = SvFLAGS(sv) & (SVf_NOK|SVp_NOK|SVf_IOK|SVp_IOK);
  }
#else
  RETVAL = looks_like_number(sv);
#endif
OUTPUT:
  RETVAL

あう。
RETVALが返ってくるのが分かるけど、中間がさっぱりだよ。
次はこっからもうちょっと見るかなー。


参考:
XS入門その4 typemap - CとPerlの型変換 - ケーズメモ404 Blog Not Found:perl - Scalar::Util::dualvar()