アルファベット順。SyntaxHighlighter( http://alexgorbatchev.com/SyntaxHighlighter/ )が対応している言語の場合はシンタックスハイライトを掛けています。
begin integer procedure mod(x, m); value x, m; integer x, m; mod := x - x div m * m; integer procedure gps(i, n, z, v); integer i, n, z, v; begin for i := 1 step 1 until n do z := v; gps := 1 end; #integer array a[1:5], b[1:5]; #integer i, z; #for i := 1 step 1 until 5 do # a[i] := i + 1; #for i := 1 step 1 until 5 do # b[i] := i + 6; #z := 0; #gps(i, 5, z, z + a[i]*b[i]); #outinteger(1, z) integer m, i, p, a, z; m := 46; i := gps(i, if i = 0 then -1 else i, p, if i = 1 then 1 else if gps(a, i, z, if a = 1 then 1 else if mod(i, a) = 0 and a < i then 0 else z) = z then (if p < m then p + 1 else i * gps(a, 1, i, -1)) else p); outinteger(1, p) end
# を付けてコメントアウトとするのは、実験に利用した処理系による Algol の独自拡張。
解説は元のページ参照。以後の言語でも、コメントで内積を計算するコードを付けるが、Algol では配列リテラルの書き方がわからなかったためコードで配列を初期化している。
C言語では、Apple による GCD(Grand Central Dispatch)のために拡張された block により、クロージャが書けるので、それを使う。参照呼びの代わりにポインタを使う。
ブロックの中で変化した値を取り出す必要がある変数には __block という修飾が必要。
演算子の左右の評価順序と副作用に依存しているので(元のページの「微妙な点」を参照)、規格には厳密には沿っていない。C++ も同様。
さらに、If(cond, a, b)
と書くと ( (cond) ? (a) : (b) )
と展開するマクロと、GPS の呼び出しについてもラップするマクロを使って、見た目に煩雑な部分を隠すようにしてみた。
(このマクロをさらに凝って #define Then )?(
/ #define Else ):(
とかやるのはどう見ても悪趣味でしょう)
#include <stdio.h> #define If(cond, th, el) ( (cond) ? (th) : (el) ) #define GPS(i, n, z, v) (gps(&(i), ^{return (n);}, &(z), ^{return (v);})) typedef int (^int_blk)(void); static int gps(int *i, int_blk n, int *z, int_blk v) { for (*i = 1; *i <= n(); ++*i) { *z = v(); } return 1; } int main(void) { /* int a_[] = {2, 3, 4, 5, 6}; int b_[] = {7, 8, 9, 10, 11}; int *a = &(a_[0]); int *b = &(b_[0]); __block int i, z = 0; gps(&i, ^{return 5;}, &z, ^{return z + a[i-1]*b[i-1];}); printf("%d\n", z); */ int m = 46; __block int i, p, a, z; /* i = gps(&i, ^{return (i == 0) ? -1 : i ;}, &p, ^{return (i == 1) ? 1 : (gps(&a, ^{return i;}, &z, ^{return (a == 1) ? 1 : ((i % a == 0) && (a < i)) ? 0 : z ;}) == z) ? ( (p < m) ? p + 1 : i * gps(&a, ^{return 1;}, &i, ^{return -1;}) ) : p ;}); */ i = GPS(i, If(i == 0, -1, i), p, If(i == 1, 1, If(GPS(a, i, z, If(a == 1, 1, If((i % a == 0) && (a < i) , 0, z ))) == z , If(p < m, p + 1 , i * GPS(a, 1, i, -1) ) , p ))); printf("%d\n", p); return 0; }
手元の環境では以下のようにしてコンパイル、実行している。
$ clang -fblocks gps.c -ldispatch $ ./a.out
#include <functional> #include <iostream> #define If(cond, th, el) ( (cond) ? (th) : (el) ) #define GPS(i, n, z, v) (gps((i), [&]{return (n);}, (z), [&]{return (v);})) typedef std::function<int()> name_int; static int gps(int &i, name_int n, int &z, name_int v) { for (i = 1; i <= n(); ++i) { z = v(); } return 1; } int main() { /* int a[] = {2, 3, 4, 5, 6}; int b[] = {7, 8, 9, 10, 11}; int i, z = 0; gps(i, [&]{return 5;}, z, [&]{return z + a[i-1]*b[i-1];}); std::cout << z << "\n"; */ int m = 46; int i, p, a, z; /* i = gps(i, [&]{return (i == 0) ? -1 : i ;}, p, [&]{return (i == 1) ? 1 : (gps(a, [&]{return i;}, z, [&]{return (a == 1) ? 1 : ((i % a == 0) && (a < i)) ? 0 : z ;} ) == z ) ? ( (p < m) ? p + 1 : i * gps(a, [&]{return 1;}, i, [&]{return -1;}) ) : p ;} ); */ i = GPS(i, If(i == 0, -1, i), p, If(i == 1, 1, If(GPS(a, i, z, If(a == 1, 1, If((i % a == 0) && (a < i) , 0, z ))) == z , If(p < m, p + 1 , i * GPS(a, 1, i, -1) ) , p ))); std::cout << p << "\n"; return 0; }
手元の環境では以下のようにしてコンパイル、実行している。
$ g++46 -std=c++0x gps.cc $ ./a.out
ラムダ式の記法はわりと簡潔なほう。参照呼びの際に実引数のほうに目印が必要という言語は、ここで実験した中では他に例がないが良い特徴だと思う。参照呼びに使う引数は言語仕様により、初期化しなければならない。
using System; delegate int name_int(); class GPS { static int Gps(ref int i, name_int n, ref int z, name_int v) { for (i = 1; i <= n(); ++i) { z = v(); } return 1; } static void Main() { /* int[] a = {2, 3, 4, 5, 6}; int[] b = {7, 8, 9, 10, 11}; int i, z; i = z = 0; Gps(ref i, () => 5, ref z, () => z + a[i-1]*b[i-1]); Console.WriteLine(z); */ int m, i, p, a, z; m = 46; i = p = a = z = 0; i = Gps(ref i, () => (i == 0) ? -1 : i, ref p, () => (i == 1) ? 1 : (Gps(ref a, () => i, ref z, () => (a == 1) ? 1 : ((i % a == 0) && (a < i)) ? 0 : z ) == z) ? ( (p < m) ? p + 1 : i * Gps(ref a, () => 1, ref i, () => -1) ) : p ); Console.WriteLine(p); } }
手元の環境では以下のようにしてコンパイル、実行している。
$ mcs GPS.cs $ mono GPS.exe
Clojure の変更可能なオブジェクトには同期機能が付いてくるが、この例の場合ただの変更可能オブジェクトとして使っている。また、@ を使って、変更可能オブジェクトの中身の読み出しは簡潔に書ける。
マクロを使うとラムダ式(Clojure では fn [] と書く)を掩蔽できる。
(defn gps [i n z v] (reset! i 1) (loop [] (if (<= @i (n)) (do (reset! z (v)) (reset! i (+ @i 1)) (recur) ) 1 ))) (defmacro gps-macro [i n z v] `(gps ~i (fn [] ~n) ~z (fn [] ~v)) ) (comment (let [a [2 3 4 5 6] b [7 8 9 10 11] i (atom 0) z (atom 0)] (gps i (fn [] 5) z (fn [] (+ @z (* (a (- @i 1)) (b (- @i 1)))))) (println @z) ) ) (comment (let [m 46 i (atom 0) p (atom 0) a (atom 0) z (atom 0)] (reset! i (gps i (fn [] (if (= @i 0) -1 @i)) p (fn [] (if (= @i 1) 1 (if (= (gps a (fn [] @i) z (fn [] (if (= @a 1) 1 (if (and (= (mod @i @a) 0) (< @a @i)) 0 @z )))) @z) (if (< @p m) (+ @p 1) (* @i (gps a (fn [] 1) i (fn [] -1))) ) @p ))))) (println @p) ) ) (let [m 46 i (atom 0) p (atom 0) a (atom 0) z (atom 0)] (reset! i (gps-macro i (if (= @i 0) -1 @i) p (if (= @i 1) 1 (if (= (gps-macro a @i z (if (= @a 1) 1 (if (and (= (mod @i @a) 0) (< @a @i)) 0 @z ))) @z) (if (< @p m) (+ @p 1) (* @i (gps-macro a 1 i -1)) ) @p )))) (println @p) )
CoffeeScript では if が値を返すことができるが、1 行に詰め込めない場合はオフサイドルールに従って配置しなければならない。ラムダ式は簡潔である。最後の引数である場合は必要ないが、途中の引数におけるラムダ式はカッコで囲む必要がある。
gps = (i, n, z, v) -> i.v = 1 while i.v <= n() z.v = v() i.v += 1 1 ### a = [2, 3, 4, 5, 6] b = [7, 8, 9, 10, 11] i = {} z = {v: 0} gps i, (-> 5), z, -> z.v + a[i.v-1]*b[i.v-1] console.log z.v ### m = 46 [i, p, a, z] = [{}, {}, {}, {}] i.v = gps i, (-> if i.v == 0 then -1 else i.v), p, -> if i.v == 1 then 1 else if (gps a, (-> i.v), z, -> if a.v == 1 then 1 else if i.v % a.v == 0 and a.v < i.v then 0 else z.v) == z.v if p.v < m then p.v + 1 else i.v * gps a, (-> 1), i, -> -1 else p.v console.log p.v
手元の環境では以下のようにして実行している。
$ ./node_modules/.bin/coffee gps.coffee
D 言語の場合、外の環境に触るクロージャは function ではなく delegate である。ラムダ式の記法では特に区別の必要はない。参照呼びの機能がある。
import std.stdio; alias int delegate() name_int; int gps(ref int i, name_int n, ref int z, name_int v) { for (i = 1; i <= n(); ++i) { z = v(); } return 1; } void main() { /* int[] a = [2, 3, 4, 5, 6]; int[] b = [7, 8, 9, 10, 11]; int i; int z = 0; gps(i, () => 5, z, () => z + a[i-1]*b[i-1]); writeln(z); */ int m = 46; int i, p, a, z; i = gps(i, () => (i == 0) ? -1 : i , p, () => (i == 1) ? 1 : (gps(a, () => i, z, () => (a == 1) ? 1 : ((i % a == 0) && (a < i)) ? 0 : z ) == z) ? ( (p < m) ? p + 1 : i * gps(a, () => 1, i, () => -1) ) : p ); writeln(p); }
F# は、ほとんど ML と同様( SML と似た部分もあれば OCaml と似た部分もある)で ref 型を使う。実引数の場所に書くラムダ式はカッコで囲む。さらにオフサイドルールに従って配置するという特徴もある。
let gps i n z v = i := 1 while !i <= n() do z := v() i := !i + 1 1 (* let a = [| 2; 3; 4; 5; 6 |] let b = [| 7; 8; 9; 10; 11 |] let i = ref 0 let z = ref 0 gps i (fun () -> 5) z (fun () -> !z + a.[!i-1]*b.[!i-1]) printfn "%d" !z *) let m = 46 let i, p, a, z = (ref 0, ref 0, ref 0, ref 0) i := gps i (fun () -> if !i = 0 then -1 else !i) p (fun () -> if !i = 1 then 1 elif (gps a (fun () -> !i) z (fun () -> if !a = 1 then 1 elif (!i % !a = 0) && (!a < !i) then 0 else !z )) = !z then if !p < m then !p + 1 else !i * gps a (fun () -> 1) i (fun () -> -1) else !p ) printfn "%d" !p
筆者の普段の環境( FreeBSD )で F# をビルドしようとすると Mono がコケてうまくできなかったので、Mac OS X に Mono をインストールして(最新の Mono をバイナリインストールすると F# も入る)試した。
Go には三項演算子がなく、それに代わるものもない。ポインタがある。
Java では構造的に全ての場合に return するならば、必ず return すると解釈される次のようなパターン
if (cond) { return foo; } else { return bar; }
が、Go では(現在のコンパイラではまだ)許されないため、テンポラリ変数を使っている。さらに Go には標準的なインデントスタイルにフォーマットしてくれるフォーマッタがあるのだが、それを使った結果以下のようなことになった。
id という、引数の値をただ返すだけという関数を定義して利用しているが、これが無いと Go 言語では評価順が保証されない( http://ksmakoto.hatenadiary.com/entry/2013/04/06/110427 も参照)。
package main import "fmt" func id(x int) int { return x } func gps(i *int, n func() int, z *int, v func() int) int { for *i = 1; *i <= n(); *i++ { *z = v() } return 1 } func main() { /* var i int a := []int{2, 3, 4, 5, 6} b := []int{7, 8, 9, 10, 11} z := 0 gps(&i, func() int { return 5 }, &z, func() int { return z + a[i-1]*b[i-1] }) fmt.Printf("%d\n", z) */ m := 46 var i, p, a, z int i = gps(&i, func() int { var tmp int if i == 0 { tmp = -1 } else { tmp = i } return tmp }, &p, func() int { var tmp int if i == 1 { tmp = 1 } else if gps(&a, func() int { return i }, &z, func() int { var tmp int if a == 1 { tmp = 1 } else if i%a == 0 && a < i { tmp = 0 } else { tmp = z } return tmp }) == id(z) { if p < m { tmp = p + 1 } else { tmp = id(i) * gps(&a, func() int { return 1 }, &i, func() int { return -1 }) } } else { tmp = p } return tmp }) fmt.Printf("%d\n", p) }
ラムダ式は波カッコで囲むだけ、return とか書く必要もない、という、ラムダ式は一番のシンプルさ。三項演算子の ? は行末に持ってこないといけない。
def gps(i, n, z, v) { for (i.v = 1; i.v <= n(); ++i.v) { z.v = v() } 1 } /* a = [2, 3, 4, 5, 6] b = [7, 8, 9, 10, 11] (i, z) = [[:], [:]] z.v = 0 gps i, { 5 }, z, { z.v + a[i.v-1]*b[i.v-1] } println z.v */ m = 46 (i, p, a, z) = [[:], [:], [:], [:]] i.v = gps i, { (i.v == 0) ? -1 : i.v }, p, { (i.v == 1) ? 1 : (gps(a, { i.v }, z, { (a.v == 1) ? 1 : ((i.v % a.v == 0) && (a.v < i.v)) ? 0 : z.v } ) == z.v ) ? ( (p.v < m) ? p.v + 1 : i.v * gps(a, { 1 }, i, { -1 }) ) : p.v } println p.v
ST モナドと STRef を使い、状態のある手続きを記述する。
以下、読解に必要な、記述テクニックについて述べる。
do 記法中の if は、次のように書く。
-- 例 (途中) main :: IO () main = do a <- getLine b <- getLine if a < b ...
do 記法の中でも、if の条件式の型は単なる Bool 型なので、そこで使う値は、この例では <- 記法を利用して「IO からひっぺがして」おかなければならない。
-- 例 (途中) main :: IO () main = do a <- getLine b <- getLine if a < b then do putStrLn "a < b" putStrLn $ "a: " ++ a putStrLn $ "b: " ++ b ...
then 節の式の型は、同様に do 記法の文脈にあるわけだから、この例の場合は IO 型でなければならないので、ネストした do 記法で書く(カッコなどはいらない)。
-- 例 main :: IO () main = do a <- getLine b <- getLine if a < b then do putStrLn "a < b" putStrLn $ "a: " ++ a putStrLn $ "b: " ++ b else putStrLn "not a < b"
then 節と同様に else 節も書かなければならないが、型が合っていれば良いので、この例のように必ずしも do 記法でなくても良い( then 節も同様)。
ループは次のようにして書く( Clojure の loop と recur を利用したループにちょっと似ている)。
-- 例 (途中) main :: IO () main = loop where loop = do ....
再帰を使うためのテンプレ的な書き方である。この loop はキーワードや組込み関数等ではなく、ただの名前であることに注意。
-- 例 main :: IO () main = loop where loop = do ln <- getLine if ln == "end" then return () else do putStrLn "cont" loop
do 記法中の if の例のように、ループの抜け出し側と繰り返し側を書く。繰り返し側では最後に loop を再帰呼び出しすればよい。
do 記法で表される式を、実引数として関数に渡す場合はカッコが必要。
gps . . . (do ... ... )
プログラム中の、評価順に依存する「微妙な点」が全てあらわになる書き方になった。特に、内容が変化するオブジェクトは、その内容を参照するのも、ほかの副作用の影響を受けるということが明示されれいる。
import Control.Monad.ST import Data.STRef gps :: STRef s Int -> ST s Int -> STRef s Int -> ST s Int -> ST s Int gps i n z v = do writeSTRef i 1 loop where loop = do i' <- readSTRef i n' <- n if i' <= n' then do writeSTRef z =<< v i'' <- readSTRef i writeSTRef i (i'' + 1) loop else return 1 {- stmain :: ST s Int stmain = do let a = [2, 3, 4, 5, 6] let b = [7, 8, 9, 10, 11] i <- newSTRef 0 z <- newSTRef 0 _ <- gps i (return 5) z (do i' <- readSTRef i z' <- readSTRef z let idx = i' - 1 return $ z' + a!!idx*b!!idx ) readSTRef z -} stmain :: ST s Int stmain = do let m = 46 i <- newSTRef 0 p <- newSTRef 0 a <- newSTRef 0 z <- newSTRef 0 writeSTRef i =<< gps i (readSTRef i >>= \i' -> return $ if i' == 0 then -1 else i') p (do i' <- readSTRef i if i' == 1 then return 1 else do tmp <- gps a (readSTRef i) z (do a' <- readSTRef a if a' == 1 then return 1 else do i'' <- readSTRef i if i'' `mod` a' == 0 && a' < i'' then return 0 else readSTRef z ) z' <- readSTRef z if tmp == z' then do p' <- readSTRef p if p' < m then return $ p' + 1 else do i'' <- readSTRef i tmp' <- gps a (return 1) i (return (-1)) return $ i'' * tmp' else readSTRef p ) readSTRef p main :: IO () main = do stToIO stmain >>= print
手元の環境の Java がまだラムダ式に対応してないため、昔ながらの無名内部クラスで書いた。
無名内部クラスの中からアクセスされる変数は final にする必要があるため、参照呼びのシミュレートのためだけでなく、RefInt 型は必要であった。
実験した中では、ラムダ式(に相当するもの)が最も冗長な言語である。
演算子の左右の評価順序なども全て仕様で決まっているため、「微妙な点」の無い言語でもある。
class RefInt { int v; } interface ThunkInt { int call(); } public class GPS { static int gps(final RefInt i, final ThunkInt n, final RefInt z, final ThunkInt v) { for (i.v = 1; i.v <= n.call(); ++i.v) { z.v = v.call(); } return 1; } public static void main(String[] args) { /* final int[] a = {2, 3, 4, 5, 6}; final int[] b = {7, 8, 9, 10, 11}; final RefInt i = new RefInt(); final RefInt z = new RefInt(); z.v = 0; gps(i, new ThunkInt(){public int call() {return 5;}}, z, new ThunkInt(){public int call() {return z.v + a[i.v-1]*b[i.v-1];}}); System.out.println(z.v); */ final int m = 46; final RefInt i = new RefInt(); final RefInt p = new RefInt(); final RefInt a = new RefInt(); final RefInt z = new RefInt(); i.v = gps(i, new ThunkInt(){public int call() {return (i.v == 0) ? -1 : i.v ;}}, p, new ThunkInt(){public int call() {return (i.v == 1) ? 1 : (gps(a, new ThunkInt(){public int call() {return i.v;}}, z, new ThunkInt(){public int call() {return (a.v == 1) ? 1 : ((i.v % a.v == 0) && (a.v < i.v)) ? 0 : z.v ;}}) == z.v) ? ( (p.v < m) ? p.v + 1 : i.v * gps(a, new ThunkInt(){public int call() {return 1;}}, i, new ThunkInt(){public int call() {return -1;}}) ) : p.v ;}}); System.out.println(p.v); } }
ラムダ式には return が必ず必要。"use strict" を全てに付けているためちょっとうるさくなっている。
"use strict" function gps(i, n, z, v){"use strict" for (i.v = 1; i.v <= n(); ++i.v) { z.v = v() } return 1 } /* var a = [2, 3, 4, 5, 6] var b = [7, 8, 9, 10, 11] var i = {} var z = {v: 0} gps(i, function(){"use strict";return 5}, z, function(){"use strict";return z.v + a[i.v-1]*b[i.v-1]}) console.log(z.v) */ var m = 46 var i = {} var p = {} var a = {} var z = {} i.v = gps(i, function(){"use strict";return (i.v === 0) ? -1 : i.v }, p, function(){"use strict";return (i.v === 1) ? 1 : (gps(a, function(){"use strict";return i.v}, z, function(){"use strict";return (a.v === 1) ? 1 : ((i.v % a.v === 0) && (a.v < i.v)) ? 0 : z.v }) === z.v) ? ( (p.v < m) ? p.v + 1 : i.v * gps(a, function(){"use strict";return 1}, i, function(){"use strict";return -1})) : p.v }) console.log(p.v)
3 項演算子が無いため、Python と同様の hack が必要。function には end が必要など、微妙に冗長。
function gps(i, n, z, v) i.v = 1 while i.v <= n() do z.v = v() i.v = i.v + 1 end return 1 end --[[ a = { 2, 3, 4, 5, 6 } b = { 7, 8, 9, 10, 11 } i = {} z = { v = 0 } gps(i, function () return 5 end, z, function () return z.v + a[i.v]*b[i.v] end) print(z.v) ]] m = 46 i, p, a, z = {}, {}, {}, {} i.v = gps(i, function () return (i.v == 0 and {-1} or {i.v})[1] end, p, function () return (i.v == 1 and {1} or {(gps(a, function () return i.v end, z, function () return (a.v == 1 and {1} or {((i.v % a.v == 0 and a.v < i.v) and {0} or {z.v} )[1]})[1] end) == z.v and {(p.v < m and {p.v + 1} or {i.v * gps(a, function () return 1 end, i, function () return -1 end)} )[1]} or {p.v} )[1]})[1] end) print(p.v)
OCaml では演算子の右辺から先に評価されるため、「微妙な点」で他の言語と左右が逆になっている。
let gps i n z v = i := 1; while !i <= n () do z := v (); i := !i + 1 done; 1;; (* let a = [| 2; 3; 4; 5; 6 |] and b = [| 7; 8; 9; 10; 11 |] and i = ref 0 and z = ref 0 in let _ = gps i (fun () -> 5) z (fun () -> !z + a.(!i-1)*b.(!i-1)) in print_endline (string_of_int !z);; *) let m = 46 and i = ref 0 and p = ref 0 and a = ref 0 and z = ref 0 in i := gps i (fun () -> if !i == 0 then -1 else !i) p (fun () -> if !i == 1 then 1 else if !z == (gps a (fun () -> !i) z (fun () -> if !a == 1 then 1 else if (!i mod !a == 0) && (!a < !i) then 0 else !z )) then if !p < m then !p + 1 else gps a (fun () -> 1) i (fun () -> -1) * !i else !p ); print_endline (string_of_int !p);;
Perl ではデフォルトで参照呼びである。PHP や Go と同じ、順序の hack がある。
sub id { $_[0] } sub gps { for ($_[0] = 1; $_[0] <= $_[1](); ++$_[0]) { $_[2] = $_[3]() } 1 } =pod my @a = (2, 3, 4, 5, 6); my @b = (7, 8, 9, 10, 11); my $i; my $z = 0; gps($i, sub { 5 }, $z, sub { $z + $a[$i-1]*$b[$i-1] }); print $z; print "\n" =cut my $m = 46; my ($i, $p, $a, $z); $i = gps($i, sub { ($i == 0) ? -1 : $i }, $p, sub { ($i == 1) ? 1 : (gps($a, sub { $i }, $z, sub { ($a == 1) ? 1 : (($i % $a == 0) and ($a < $i)) ? 0 : $z }) == $z) ? ( ($p < $m) ? $p + 1 : id($i) * gps($a, sub { 1 }, $i, sub { -1 }) ) : $p }); print $p; print "\n"
PHP 5.3 から使えるようになった無名関数を使っている。PHP には参照呼びがある。
式の評価順において、どうやら Go 言語と同じ特性があるので、同様の値をただ返すだけの関数を使っている。
三項演算子の結合順が、他の言語と逆のため、カッコがちょっと多い(逆に省略できる場合もこのプログラム中にはあるのだが、そちらは省略していない)。
<?php function id($x) { return $x; } function gps(&$i, $n, &$z, $v) { for ($i = 1; $i <= $n(); ++$i) { $z = $v(); } return 1; } /* $a = [2, 3, 4, 5, 6]; $b = [7, 8, 9, 10, 11]; $z = 0; gps($i, function(){return 5;}, $z, function()use(&$a, &$b, &$i, &$z){return $z + $a[$i-1]*$b[$i-1];}); printf("%d\n", $z); */ $m = 46; $i = gps($i, function()use(&$i){return ($i === 0) ? -1 : $i ;}, $p, function()use(&$m, &$i, &$p, &$a, &$z){return ($i === 1) ? 1 : ( (gps($a, function()use(&$i){return $i;}, $z, function()use(&$i, &$a, &$z){return ($a === 1) ? 1 : ( (($i % $a === 0) && ($a < $i)) ? 0 : $z );}) === $z) ? ( ($p < $m) ? $p + 1 : id($i) * gps($a, function(){return 1;}, $i, function(){return -1;}) ) : $p );}); print "$p\n"; ?>
三項演算子がない(実は最近はあるが、順序が他と違うため避けた)のでトリッキーな書き方をしている。
lambda には文が書けないという制限があるが、and と or でつないだ大きな式であるため書けている。
class Ref: def __init__(self): self.v = None def gps(i, n, z, v): i.v = 1 while i.v <= n(): z.v = v() i.v += 1 return 1 """ a = [2, 3, 4, 5, 6] b = [7, 8, 9, 10, 11] i, z = Ref(), Ref() z.v = 0 gps(i, lambda: 5, z, lambda: z.v + a[i.v-1]*b[i.v-1]) print(z.v) """ m = 46 i, p, a, z = Ref(), Ref(), Ref(), Ref() i.v = gps(i, lambda: (i.v == 0 and [-1] or [i.v])[0], p, lambda: (i.v == 1 and [1] or [(gps(a, lambda: i.v, z, lambda: (a.v == 1 and [1] or [((i.v % a.v == 0 and a.v < i.v) and [0] or [z.v] )[0]])[0]) == z.v and [(p.v < m and [p.v + 1] or [i.v * gps(a, lambda: 1, i, lambda: -1)] )[0]] or [p.v] )[0]])[0]) print(p.v)
あるいは「Python らしく」書き直すと以下のようになる。実は最も解読しやすいのはこれかもしれない。
class Ref: def __init__(self): self.v = None def gps(i, n, z, v): i.v = 1 while i.v <= n(): z.v = v() i.v += 1 return 1 def main(): m = 46 i, p, a, z = Ref(), Ref(), Ref(), Ref() def func1(): if i.v == 0: return -1 else: return i.v def func2(): if i.v == 1: return 1 elif gps(a, lambda: i.v, z, func3) == z.v: if p.v < m: return p.v + 1 else: return i.v * gps(a, lambda: 1, i, lambda: -1) else: return p.v def func3(): if a.v == 1: return 1 elif i.v % a.v == 0 and a.v < i.v: return 0 else: return z.v i.v = gps(i, func1, p, func2) print(p.v) main()
ruby 1.9 から使えるようになった記法を使っている。ラムダ式は他の言語と比べて簡潔な方である。end は if の最後の end である。他の言語では p という変数名を使っているが、ruby には p メソッドがあるので q を代わりに使っている。
class Ref attr_accessor :v def initialize *v case v.size when 0 @v = nil when 1 @v = v[0] else @v = v end end end def gps i, n, z, v i.v = 1 while i.v <= n.() do z.v = v.() i.v += 1 end 1 end =begin a = [2, 3, 4, 5, 6] b = [7, 8, 9, 10, 11] i, z = Ref.new, Ref.new(0) gps i, ->{5}, z, ->{z.v + a[i.v-1]*b[i.v-1]} p z.v =end m = 46 i, q, a, z = Ref.new, Ref.new, Ref.new, Ref.new i.v = gps i, ->{if i.v == 0 then -1 else i.v end}, q, ->{if i.v == 1 then 1 else if (gps a, ->{i.v}, z, ->{if a.v == 1 then 1 else if (i.v % a.v == 0) && (a.v < i.v) then 0 else z.v end end}) == z.v then if q.v < m then q.v + 1 else i.v * (gps a, ->{1}, i, ->{-1}) end else q.v end end} p q.v
Scala には名前呼びがある。実際のところクロージャ渡しだが、名前呼びの実引数の場所には単に式を書けば、それがクロージャを書いたことになるし(特に、Scala では return を普通は明示的に書かなければならないが、それを省略できる)、名前呼びの引数を参照すると、暗黙のうちに () が付いていることになってクロージャの呼び出しが走る。
参照呼びは無いので、ラッパーを経由している。
class RefInt(var v: Int) {} def gps(i: RefInt, n: => Int, z: RefInt, v: => Int): Int = { i.v = 1 while (i.v <= n) { z.v = v i.v += 1 } return 1 } /* val a = Array(2, 3, 4, 5, 6) val b = Array(7, 8, 9, 10, 11) val i = new RefInt(0) val z = new RefInt(0) gps(i, 5, z, z.v + a(i.v-1)*b(i.v-1)) println(z.v) */ val m = 46 val (i, p, a, z) = (new RefInt(0), new RefInt(0), new RefInt(0), new RefInt(0)) i.v = gps(i, if (i.v == 0) -1 else i.v, p, if (i.v == 1) 1 else if (gps(a, i.v, z, if (a.v == 1) 1 else if ((i.v % a.v == 0) && (a.v < i.v)) 0 else z.v ) == z.v) if (p.v < m) p.v + 1 else i.v * gps(a, 1, i, -1) else p.v ) println(p.v)
処理系には Gauche を使った。参照型の代わりにリストを使った。SICP 流には、box と unbox とでもして抽象化するべきかとも思ったが(実際そういう関数がある処理系もある)、car と書く方が短いのでこうした。
マクロも使って lambda を掩蔽してみている。
;(use gauche.array) (define (gps i n z v) (set-car! i 1) (while (<= (car i) (n)) (set-car! z (v)) (set-car! i (+ (car i) 1)) ) 1 ) (define-syntax gps-macro (syntax-rules () ((gps-macro i n z v) (gps i (lambda () n) z (lambda () v)) ))) #| (define (main args) (let ((a #,(<array> (1 6) 2 3 4 5 6)) (b #,(<array> (1 6) 7 8 9 10 11)) (i (cons 0 ())) (z (cons 0 ())) ) (gps i (lambda () 5) z (lambda () (+ (car z) (* (array-ref a (car i)) (array-ref b (car i)))))) (print (car z)) )) |# #| (define (main args) (let ((m 46) (i (cons 0 ())) (p (cons 0 ())) (a (cons 0 ())) (z (cons 0 ())) ) (set-car! i (gps i (lambda () (if (= (car i) 0) -1 (car i))) p (lambda () (if (= (car i) 1) 1 (if (= (gps a (lambda () (car i)) z (lambda () (if (= (car a) 1) 1 (if (and (= (modulo (car i) (car a)) 0) (< (car a) (car i))) 0 (car z) )))) (car z)) (if (< (car p) m) (+ (car p) 1) (* (car i) (gps a (lambda () 1) i (lambda () -1))) ) (car p) ))))) (print (car p)) )) |# (define (main args) (let ((m 46) (i (cons 0 ())) (p (cons 0 ())) (a (cons 0 ())) (z (cons 0 ())) ) (set-car! i (gps-macro i (if (= (car i) 0) -1 (car i)) p (if (= (car i) 1) 1 (if (= (gps-macro a (car i) z (if (= (car a) 1) 1 (if (and (= (modulo (car i) (car a)) 0) (< (car a) (car i))) 0 (car z) ))) (car z)) (if (< (car p) m) (+ (car p) 1) (* (car i) (gps-macro a 1 i -1)) ) (car p) )))) (print (car p)) ))
現在配布されている SML# を使うには 32 ビット環境が必要だった。
(* gps.smi _require "basis.smi" *) fun gps i n z v = ( i := 1; while !i <= n () do ( z := v (); i := !i + 1); 1); (* let val a = Array.fromList [2, 3, 4, 5, 6] val b = Array.fromList [7, 8, 9, 10, 11] val i = ref 0 val z = ref 0 in gps i (fn () => 5) z (fn () => !z + Array.sub(a, !i-1)*Array.sub(b, !i-1)); print (Int.toString (!z)); print "\n" end *) let val m = 46 val i = ref 0 val p = ref 0 val a = ref 0 val z = ref 0 in i := gps i (fn () => if !i = 0 then ~1 else !i) p (fn () => if !i = 1 then 1 else if (gps a (fn () => !i) z (fn () => if !a = 1 then 1 else if (!i mod !a = 0) andalso (!a < !i) then 0 else !z )) = !z then if !p < m then !p + 1 else !i * gps a (fn () => 1) i (fn () => ~1) else !p ); print (Int.toString (!p)); print "\n" end
Visual Studio 2008 以降の VB.NET であれば次のように書ける。VB には参照呼びがある。Iif は関数だが、ここで使っている If 演算子はちゃんと短絡評価になる。( If 演算子、ラムダ式とも VS 2008 以降であり、Mono プロジェクトの mono-basic の VB は 2005 相当のため、このプログラムの実行は無理)
1 論理行に収まる Function であれば Return を書かなくてもよいなどの点は、他の言語より関数的だったりする。参照呼びに使う変数は初期化しておかないと、(ウォーニングも出るが)結果が得られなくなる。
Imports System Public Module GPS Function Gps(ByRef i As Integer, n As Func(Of Integer), ByRef z As Integer, v As Func(Of Integer)) i = 1 While i <= n() z = v() i += 1 End While Return 1 End Function Sub Main() Rem Dim a() = {2, 3, 4, 5, 6} Rem Dim b() = {7, 8, 9, 10, 11} Rem Dim i = 0 Rem Dim z = 0 Rem Gps(i, Function() 5, z, Function() z + a(i-1)*b(i-1)) Rem Console.WriteLine(z) Dim m = 46 Dim i = 0 Dim p = 0 Dim a = 0 Dim z = 0 i = Gps(i, Function() If(i = 0, -1, i), p, Function() If(i = 1, 1, If(Gps(a, Function() i, z, Function() If(a = 1, 1, If(i Mod a = 0 AndAlso a < i, 0, z ))) = z, If(p < m, p + 1, i * Gps(a, Function() 1, i, Function() -1) ), p ))) Console.WriteLine(p) End Sub End Module
手元の Windows 環境では次のようにしてコンパイルした。
>C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\vbc.exe GPS.vb