真偽値の大小比較 [言語比較]
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 の標準ではないのかな?)