SSブログ
言語比較 ブログトップ

真偽値の大小比較 [言語比較]

Ruby

irb(main):001:0> true > false
NoMethodError: undefined method `>' for true:TrueClass
        from (irb):1

Python

>>> True > False
True
>>> True < False
False

OCaml

# true > false;;
- : bool = true
# true < false;;
- : bool = false

SML

- true > false;
stdIn:1.6 Error: overloaded variable not defined at type
  symbol: >
  type: bool

uncaught exception Error
  raised at: ../compiler/TopLevel/interact/evalloop.sml:52.48-52.56
             ../compiler/TopLevel/interact/evalloop.sml:35.55

Haskell

Hugs.Base> True > False
True
Hugs.Base> True < False
False

Scala

scala> true > false
unnamed0: Boolean = true

scala> true < false
unnamed1: Boolean = false

Lua

> if true > false then  end
stdin:1: attempt to compare two boolean values
stack traceback:
        stdin:1: in main chunk
        [C]: ?

Scheme (Gauche)

gosh> (> #t #f)
*** ERROR: real number required: #t
Stack Trace:
_______________________________________

Common Lisp (SBCL)

* (> T NIL)

debugger invoked on a TYPE-ERROR: The value T is not of type NUMBER.

Windows PowerShell

PS C:\> $a; $a.getType()
True

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Boolean                                  System.ValueType


PS C:\> $b; $b.getType()
False

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Boolean                                  System.ValueType


PS C:\Documents and Settings\ether> if ($a > $b) { echo "True > False"} else { echo "True < False" }
True < False

Erlang

Eshell V5.5.5  (abort with ^G)
1> true > false.
true
2> true < false.
false

JavaScript

js> true > false
true
js> true < false
false

Excel 関数

PHP

PS D:\> cat tryme.php
<?php
  if (true > false) { echo "true > false\n"; }
  if (true < false) { echo "true < false\n"; }
?>
PS D:\> php tryme.php
true > false

真偽値が組み込み型でないような言語は実験から除外している。
まとめると、

真偽値は大小比較できる:
Python, OCaml, Haskell, Scala, PowerShell, Erlang, JavaScript, Excel, PHP

真偽値は大小比較できない:
Ruby, SML, Lua, Scheme, Common Lisp,

直感的には真偽値に大小なんてない気がするので大小比較できるほうが多数派っぽいというのにやや驚き。

特に Haskell なんかは「Bool 型って Ord クラスでしょ」っていうのを設計者が明示的に決定したであろうわけで、その意図が知りたい気がする。

なお大小比較できるもののうち Windows PowerShell だけは真<偽となるという点で特異だが、これは PowerShell はシェル言語なのでコマンドの成功が 0 であり、失敗がそれ以外であるという慣習とあわせたい部分があるのだと思う。
Unix のシェルでも true コマンドの戻り値は 0 で false コマンドの戻り値は 1 だ。


情報隠蔽とパターンマッチ [言語比較]

情報隠蔽とパターンマッチを巡って考えたことなど。

OCaml にはシグニチャという仕組みがあってオブジェクト指向言語でいうところのインターフェイスのようなものとして使われる。

例として次のようなスタックの実装を考えてみる。

module StackImpl =
struct
  type 'a t = 'a list
  let empty = []
  let push x s = x::s
  let pop (x::xs) = (x, xs)
end

モジュール StackImpl は単純にリストとしてスタックを定義している。

# let s = StackImpl.empty;;
val s : 'a list = []
# let s = StackImpl.push 'a' s;;
val s : char list = ['a']
# let s = StackImpl.push 'b' s;;
val s : char list = ['b'; 'a']
# let s = StackImpl.push 'c' s;;
val s : char list = ['c'; 'b'; 'a']
# match s with [] -> '*' | x::xs -> x;;
- : char = 'c'

モジュールが定義している操作は push と pop だけだが、内部表現はリストなので型はそのように表示されるし最後の例のようにリストとしてのパターンマッチにかけることもできる。

しかし情報隠蔽の考え方からするとスタックは内部実装を晒さずに操作だけが定義されるのが望ましい。そこで使われるのがシグニチャである。

module type StackSig =
sig
  type 'a t
  val empty : 'a t
  val push : 'a -> 'a t -> 'a t
  val pop : 'a t -> 'a * 'a t
end

module MyStack = (StackImpl : StackSig)

StackSig がシグニチャで、スタックを操作するインターフェイスのみを定義したものである。ここでは型として 'a t を定義しているのみでそれがリストだということはどこにも表現されない。

MyStack はこのシグニチャを先の StackImpl に適用している。このようにすると MyStack の使用者にはそれがリストによる実装であるということが隠蔽される。

# let s = MyStack.empty;;
val s : 'a MyStack.t = <abstr>
# let s = MyStack.push 'a' s;;
val s : char MyStack.t = <abstr>
# let s = MyStack.push 'b' s;;
val s : char MyStack.t = <abstr>
# let s = MyStack.push 'c' s;;
val s : char MyStack.t = <abstr>
# match s with [] -> '*' | x::xs -> x;;
Characters 13-15:
  match s with [] -> '*' | x::xs -> x;;
               ^^
This pattern matches values of type 'a list
but is here used to match values of type char MyStack.t

このようにすることの利点は勿論実装を取り替えても使用者側のコードには影響がないということを保証できるということである。
一方でこうすることで OCaml の強力なツールであるパターンマッチは使えなくなってしまう。
別の言い方をすると一般にパターンマッチというのは内部構造を隠蔽しないことによってのみ可能になるものだということだ。つまりパターンマッチは情報隠蔽と相性が悪い。例えば以下のような疑似コードは OCaml では書けない。

match push 'a' (push 'b' empty) with
| push x s -> ...(* ここで x='a', s='b'だけのスタック*)

同様にパターンマッチは「構造のある」データに対してしか使えないということもいえる。例えばリストやタプルは構造を持ったデータだが、整数はそうではない。従って以下の例の前者は可能だが後者はそうではない。

let rec sum = function
| [] -> 0
| x::xs -> x + sum xs
let fact = function
| 1 -> 1
| (1 + n) as m -> m * fact n

実は Haskell ではこれに相当することは可能である。しかし加算以外とか整数以外に使えるような一般的なものではないようなので大した使い道はない。

fact 1 = 1
fact m @ (n + 1) = m * fact n

Scala で最近導入された extractor という概念は上記のような問題に対する試みと考えられる。Haskell 風の n+k パターンは Scala では下記のように書ける。

object add1 {
  def apply(x:Int):Int = x + 1
  def unapply(x:Int):Option[Int] = Some(x - 1)
}

object Fact {
  def fact(x:Int):Int = x match {
      case 1 => 1
      case m @ add1(n) => m * fact(n)
  }
  def main(args: Array[String]) = {
    Console.println(fact(args(0).toInt))
  }
}

ここで add1 は擬似的な関数オブジェクトである。もともと Scala の関数オブジェクトにはメソッドとして apply というのがあり、関数適用をした場合にこれが暗黙に呼ばれるのだが、その逆を行う unapply というメソッドを定義するとこのようなパターンマッチが可能となる。この unapply メソッドを extractor と呼ぶ。(実は現行のバージョンでは上記の m が期待通りの値に束縛されないが、次のバージョンで修正されるそうだ)

最初のスタックの例も下記のように書くことが出来る。

abstract class Stack[a] {
  def push(x:a): Stack[a]
  def pop(): Pair[a, Stack[a]]
}

class ListStack[a](l:List[a]) extends Stack[a] {
  def push(x:a) = {
    new ListStack(x::l)
  }
  def pop() = {
    Pair(l.head, new ListStack(l.tail))
  }
  override def toString = l.toString
}

 // 以下の2つを generic にするにはどう書いたらいいのか分からなかった
object push {
  def apply(x:int, s:Stack[int]) = s.push(x)
  def unapply(s:Stack[int]) = Some(s.pop)
}

object pop {
  def apply(s:Stack[int]) = s.pop
  def unapply(x:int, s:Stack[int]) = s.push(x)
}

object Main {
  def main(args: Array[String]) = {
    var s = new ListStack[Int](List())
    s = s.push(1)
    s = s.push(2)
    s = s.push(3)
    s = s.push(4)
    s match {
      case push(x, push(y, s)) => {
        Console.println("x = " + x)
        Console.println("y = " + y)
        Console.println("s = " + s)
        /*
          x = 4
          y = 3
          s = List(2,1)
        */
      }
    }
    Console.println("")
  }
}

ここで object push と object pop はインターフェイス Stack の操作のみを使用していて実装には依存しない。そのような関数オブジェクトを使って case push(x, push(y, s)) のようなパターンマッチを行うことができている。ここでは情報隠蔽とパターンマッチが両立しているといえる。

Scala のアプローチには問題らしきものもある。特に構文上もともとの加算演算子やメソッド呼び出しとは異なる構文を使わざるを得ない。また副作用や破壊的な操作を行う関数の unapply は書けないか少なくとも書きにくいだろうが、これは仕方ないと思われる。

そして extractor という概念はもう一つ興味深い問題を導入するように思われる。例えば絶対値を計算する abs 関数があったとしてその unapply はどのように書くべきだろうか。あるいは2項を取る add 関数は?

2007-07-25追記:

上記のコード中で「generic にするにはどう書いたらいいのか分からなかった」としている部分はその後以下のように書けばよいことが分かりました。

object push {
  def apply[a](x:a, s:Stack[a]) = s.push(x)
  def unapply[a](s:Stack[a]) = Some(s.pop)
}

object pop {
  def apply[a](s:Stack[a]) = s.pop
  def unapply[a](x:a, s:Stack[a])= s.push(x)
}

What Makes Lisp Different? [言語比較]

Higher-Order Perl の Mark Jason Dominus は前書きの中で以下のように書いている。

例えば、Peter Norvig の本 Paradigms of Artificial Intelligence Programming には What Makes Lisp Different? というセクションがあって Lisp の7つの特徴について書いている。Perl はそのうちの6つを共有している。C はゼロだ。

この前書きではそのうち3つしか数え上げていないので他は何なんだろう?と思っていたのだが、以下のものだった。

1. Built-in Support for Lists
2. Automatic Storage Management
3. Dynamic Typing
4. First-Class Functions
5. Uniform Syntax
6. Interactive Environment
7. Extensibility

MJD は多分5以外のすべてにチェックを入れたのだろうと推測する。

1はリストのビルトインサポートということなのだが、この場合のリストというのは関数型のリストのことで、Perl の配列なんかとはちょっと違う。なのでちょっと甘い基準を適用しているのだろう。(たぶん「ビルトインじゃないけど実装はできるでしょ」的な)

7の拡張性はそもそも基準として曖昧な部分があるが、Norvig は例としてマクロを挙げているのでこれも Perl に適用するには苦しいところがある。

そんなわけで厳しい基準を適用すると本当は4つくらいだと思う。

ちなみに6のInteractive Environmentにはインタラクティブシェルという意味と、シンボルテーブルへの実行時の参照という意味があって、素の Perl に適用されるのは後者だ。これは前者は言語自体の問題ではないのでOKだろう。

さて、この7つの特徴をめぼしい言語について適用してチェックをつけてみた。いい悪いはともかくとしてその言語の Lisp 度を見る基準になるかもしれない。(6は先の後者の意味で評価している)

Perl Lua Ruby Python JavaScript OCaml Tcl Scala Haskell SML awk
1. Built-in Support for Lists × × × × × × ×
2. Automatic Storage Management
3. Dynamic Typing × × × ×
4. First-Class Functions × ×
5. Uniform Syntax × × × × × × × × × ×
6. Interactive Environment × × × × ×
7. Macro × × × × × × × × × ×

厳しいほうの基準を適用すると Perl の Lisp 度は Lua, Python, Ruby, JavaScript なんかと違わないということがわかる。
Higher-Order Perl みたいな本をこれらの言語で書くということも不可能ではないということだ。


Python vs Tcl vs Perl5 [言語比較]

http://www.python.org/doc/Humor.html#vowels

Python のサイトにあった(10年前の)ジョーク。単に名前について書いているようで言語の特徴についても書いているという。うまいなあ。


関数やメソッドがない場合の動作を定義する方法の比較 [言語比較]

Ruby

メソッド

method_missing メソッドを定義することで可能。呼び出し時にメソッドが無かった場合はこれが呼ばれる。

irb(main):005:0> class Foo
irb(main):006:1>   def method_missing(name, *args)
irb(main):007:2>     puts "#{name}: #{args.inspect}"
irb(main):008:2>   end
irb(main):009:1> end
nil
irb(main):010:0> Foo.new.foo(1,2,3)
foo: [1, 2, 3]
nil
irb(main):011:0>
関数

関数についても同様。

irb(main):001:0> def method_missing(name, *args)
irb(main):002:1>   puts "#{name}: #{args.inspect}"
irb(main):003:1> end
nil
irb(main):004:0> foo(1,2,3)
foo: [1, 2, 3]
nil

Perl

メソッド

AUTOLOAD を定義してあげるとできる。

{
  package Foo;

  sub new { bless {}, shift; }

  sub AUTOLOAD {
    shift;
    print "$AUTOLOAD: ";
    print "@_\n";
  }

}

$foo = new Foo;
$foo->foo(1, 2, 3);
関数

関数のサーチは最終的に UNIVERSAL パッケージにたどり着くので、そこで AUTOLOAD を定義すればよい。

{
  package UNIVERSAL;

  sub AUTOLOAD {
    print "$AUTOLOAD: ";
    print "@_\n";
  }
}

foo(1, 2, 3);

Python

メソッド

__getattr__ を定義することで可能。属性の値として関数を返すというところに注意。

>>> class Foo:
...   def __getattr__(self, name):
...     def tmpf(*args):
...       print name+":",
...       for x in args:
...         print x,
...     return tmpf
...
>>> Foo().foo(1,2,3)
foo: 1 2 3
関数

関数についてはわからない。

Tcl

メソッド

Tcl には標準のオブジェクト指向機構はない。古典的な「プロシージャ=オブジェクト」方式の場合、そもそもメソッドのディスパッチをユーザレベルで書くことになるのである意味どうにでもなる。

関数

プロシージャが見つからない場合は unknown コマンドが呼ばれる。

% proc unknown {name args} {
    puts [concat "$name: " $args]
  }
% foo 1 2 3
foo: 1 2 3

Lua

メソッド

テーブルにメタメソッドを定義する。Python と同様に関数を返すやり方。

> foo = {}
> setmetatable(foo, {
>>   __index = function(t, k)
>>    return function(...)
>>      io.write(k..": ")
>>      for i, v in ipairs{...} do
>>        io.write(v.." ")
>>      end
>>      print()
>>    end
>>   end
>> })
> foo.foo(1,2,3)
foo: 1 2 3
関数

_G にメタメソッドを定義する。

> setmetatable(_G, {
>>  __index = function(t, k)
>>   return function(...)
>>     io.write(k..": ")
>>     for i, v in ipairs{...} do
>>       io.write(v.." ")
>>     end
>>     print()
>>   end
>>  end
>> })
> foo(1,2,3)
foo: 1 2 3

Lua は「未定義のグローバル変数の参照は nil を返す」という既定動作があるが、Lua は関数と変数の名前空間が同じなので上記を実施した場合影響を受けてしまう。

JavaScript

prototype プロパティという仕組みはあるのだが、上記の他の言語に対応するコードは書けないような気がする。

Lisp

わかりません(こればっかり)。

PHP

(2006-11-22 に追加)

メソッド

__call メソッドを定義する。

<?php
  class Foo {
    public function __call($name, $args) {
      print("$name:\n");
      print_r($args);
    }
  }
  $o = new Foo;
  $o->foo(1, 2, 3);

?>
関数

関数については不明。


変数へのアクセスをフックする方法の比較 [言語比較]

Tcl

ビルトインの trace コマンドで変数がアクセスされたときに呼び出されるプロシージャを登録できる。今回比較した言語の中でもっとも真っ当かつ充実している。プロシージャ名を与える仕様なのでプロシージャを予め作る必要があり、フックしたい変数が1つしか無い場合などは後述の Ruby と比べるとやや面倒。

proc hook {name1 name2 op} {
  if {$op == "write"} {
    upvar #0 $name1 var
    puts "$op $name1: $var"
  } else {
    puts "$op $name1"
  }
}

trace add variable foo {read write} hook

set foo "hello world"
set bar $foo

実行結果は以下。

F:\>tclsh tryme.tcl
write foo: hello world
read foo

Ruby

組み込み関数の trace_var でグローバル変数への書き込みをフックできる。ブロックを使った書き方は簡潔でよい。ただしグローバル変数にしか使えず、書き込み時のみなのであまり面白い使い道はなさそう。ブロックに与えられる引数は変更後の値のみで変数名がない(変数名を使いたければ変数ごとに別々のブロックを書いてあげる必要がある)。

trace_var(:$foo) {|value|
  print "write: #{value}"
}

$foo = "hello world"

Perl

tie を使えばできる。ある意味自由度は高いが面倒。フック関数側での変数名の使用は素直にやる方法はない。もっと工夫すれば何かやりようはあるのかも。

{
  package trace;

  sub TIESCALAR {
    my ($pkg, $value) = @_;
    bless \$value, $pkg;
  }

  sub FETCH {
    my $self = shift;
    print "read\n";
    return $$self;
  }

  sub STORE {
    my $self = shift;
    my $value = shift;
    print  "write: $value\n";
    $$self = $value
  }
}

tie $foo, trace, $foo;

$foo = "hello world";
$bar = $foo;
print $bar;

Lua

グローバル変数テーブル _G を置き換えてしまってテーブルアクセスメタメソッドを書いてあげればどうにかできる。グローバル変数にしか使えない。そのままだとすべてのグローバル変数のアクセスで呼び出されてしまうので関数側で適宜条件分岐とかを書いてあげる必要がある。

local real_globals = {}

setmetatable(_G, {
  __index = function(t, k)
    if (k == "foo") then
      print("read " .. k)
    end
    return real_globals[k]
  end,
  __newindex = function(t, k, v)
    if (k == "foo") then
      print("write " .. k .. ": " .. v)
    end
    real_globals[k] = v
  end
})

foo = "hello world"
bar = foo

Python

わからない。

JavaScript

わからない。

Lisp

わからない。


ローカル変数のテーブルを実行時に取得する方法の比較 [言語比較]

Python

locals 関数を使う。

>>> def f(x):
...   y=1
...   print locals()
...
>>> f(0)
{'y': 1, 'x': 0}
>>>

Tcl

info locals コマンドを使う。

% proc f {x} {
    set y 1
    foreach i [info locals] {
      puts "$i: [set $i]"
    }
  }
% f 0
x: 0
y: 1

Lua

debug.getlocal 関数を使う。

> function f(x)
>>   local y = 1
>>   a = 1 -- これはグローバル変数とした
>>   while true do
>>     local name, value = debug.getlocal(1, a)
>>     if not name then break end
>>     print(name, value)
>>     a = a + 1
>>   end
>> end
> f(0)
x       0
y       1

Perl

できない。 my 変数は実行時のシンボルテーブルに入らない。
ハッシュ %:: または %main:: でグローバル変数のシンボルテーブルにはアクセスできる。

JavaScript

できない?

関数の仮引数にアクセスする方法はあった(変数名は取得できない)。

js> function f(x) {
      var y = 1;
      print(arguments[0]); /* 最初の仮引数を表示 */
    }
js> f(0);
0
js> f(13);
13
js>

Ruby

Lisp

(variables) で取得できるのはグローバルのみ?(Perlと同じ?)
(というか (variables) という関数は Common Lisp の標準ではないのかな?)


言語比較 ブログトップ

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。