Petite Chez Schemeをインストール
tar -xzvf pcsv8.4-ti3osx.tar.gz cd csv8.4/costom ./configure --installprefix=~/local --installman=~/local/share/man make make install
すると petite と scheme-script がインストールされる。root権限にchownしようとしたりしていろいろエラーが出るがとりあえず無視する。すると、
$ petite cannot find compatible petite.boot in search path "/Users/kaki/lib/csv%v/%m:/usr/lib/csv%v/%m:/usr/local/lib/csv%v/%m"
と言われる。bootパスが間違っているので
petite -b ~/local/lib/csv8.4/ta6osx/petite.boot
で起動できる。そこで
cd ~/local/bin mv petite petite8.4
petite
#!/bin/sh exec petite8.4 -b ~/local/lib/csv8.4/ti3osx/petite.boot "$@"
petite-script
#!/bin/sh exec petite8.4 -b ~/local/lib/csv8.4/ti3osx/scheme-script.boot --script "$@"
としてみた。scheme-script いらなさそうだけどとりあえず保留。
任意の型の引数を無限に消費する空腹関数はHaskell(GHC)では書けないのか?
Schemeでいうと
(define (hungry x) hungry)
みたいなやつ。
ここまではできた。参考: https://github.com/jstolarek/sandbox/blob/master/haskell/Hungry.hs
Prelude> :set -XRankNTypes Prelude> data Hungry = Hungry { (%) :: forall b. b -> Hungry } Prelude> let fix f = f $ fix f Prelude> let hungry = fix id :: Hungry Prelude> :t hungry % 42 % 'c' % [] hungry % 42 % 'c' % [] :: Hungry
unHungry (上では (%)
) 無しでは書けないのかなあ。
(追記 18:38)
できた、のか?
参考:可変長引数をHaskellで。 - ここにタイトルを入力|
Prelude> class Hungry f where hungry :: a -> f Prelude> instance Hungry a => Hungry (f -> a) where hungry _ = hungry Prelude> :t hungry 42 'c' [] hungry 42 'c' [] :: Hungry t => t
型クラスにするとそれっぽいものが何故かできた。何故だ?
はじめてのQuineリレー
概要
昨日、自分自身を出力するHaskellプログラムを生成するOCamlプログラムを生成するSchemeプログラムを生成するRubyプログラムを生成するJavaScriptプログラムを生成するCプログラムを書いた。
#include<stdio.h> int main(){char*g="var p=print;p('puts %%^(format #t!Printf.printf~s~s!(x->string`main=putStr%%S)^.tr *?!...?#;p <<_;$><<?)');p('#include<stdio.h>');print(%cint main(){char*g=%c%s%c;printf(g,47,34,g,34,47);}%c.source);p('_')";printf(g,47,34,g,34,47);}
以下のように動作する。Makefileは後述。
$ make diff clang -std=c99 -Wall -W -Werror -pedantic -o quine-relay-c start.c && ./quine-relay-c > quine-relay.js v8 --use_strict quine-relay.js > quine-relay.rb ruby quine-relay.rb > quine-relay.scm gosh quine-relay.scm > quine-relay.ml ocaml quine-relay.ml > quine-relay.hs runghc quine-relay.hs > goal.c diff -u start.c goal.c $ wc start.c quine-relay.* 2 9 270 start.c 0 8 287 quine-relay.hs 0 13 402 quine-relay.js 0 8 304 quine-relay.ml 4 15 360 quine-relay.rb 2 11 333 quine-relay.scm 8 64 1956 total
動機
「SchemeやRubyが得意でHaskellやOCamlも書けます!」と堂々と言えるようになりたい。ならばそれらの言語でQuineリレーをしよう。思い付いてしまったからには、書かねばなるまい。そう思っていた時期もあった。
かのように決心して書き始めたのだが、存外に難しくはなく、その日のうちにできてしまった。
構想
コンセプト。Quineリレー自体は偉大なる先人が凄いのを成し遂げていらっしゃるし、自分はQuineリレーには初挑戦なので、今回は得意言語のアッピルに重点を置いて、とりあえず高級言語数個での完成を目指すことにした。次に順序が問題となる。Brainf**kやBefungeを入れるでもないのに順序が任意では趣に欠けるというもの。これはなんとなくCを起点にして触った順にした。結果、自分のプログラミングの学びの歴史を辿り最後は初心に帰るという個人的に気持ち悪い大層趣のあるプログラムができあがった。あと、今回の主旨に鑑みて、特定の言語で何重にもエスケープして残りの言語は文字列を出力するだけ、みたいなヒキョウなことはなるべくしないようにした。
もう一つ、追加の制約としてCコンパイラのオプションに -std=c99 -Wall -W -Werror -pedantic
を付けることにした。これでこのプログラムがコンパイラも黙る程の真っ当なプログラムであることは確定的に明らか。奇妙な挙動や拡張に依存していたら、コンパイラによってバラバラに引き裂かれることになる。
ついでにちょっと縮めた。
感想
付録
.PHONY: all clean diff CC = clang CFLAGS = -std=c99 -Wall -W -Werror -pedantic JavaScript = v8 --use_strict Ruby = ruby Scheme = gosh OCaml = ocaml Haskell = runghc DIFF = diff -u START = start.c GOAL = goal.c Q = quine-relay C_EXE = $(Q)-c all: $(GOAL) diff: all $(DIFF) $(START) $(GOAL) $(Q).js: $(START) $(CC) $(CFLAGS) -o $(C_EXE) $< && ./$(C_EXE) > $@ $(Q).rb: $(Q).js $(JavaScript) $< > $@ $(Q).scm: $(Q).rb $(Ruby) $< > $@ $(Q).ml: $(Q).scm $(Scheme) $< > $@ $(Q).hs: $(Q).ml $(OCaml) $< > $@ $(GOAL): $(Q).hs $(Haskell) $< > $@ clean: rm -f $(GOAL) $(C_EXE) $(Q).js $(Q).rb $(Q).scm $(Q).ml $(Q).hs
あと各処理系のバージョン。Gaucheは開発版の何か。
$ clang -v clang version 3.5.0 (tags/RELEASE_350/final) Target: x86_64-apple-darwin13.4.0 Thread model: posix $ echo -n | v8 V8 version 3.25.30 [sample shell] > $ ruby -v ruby 2.2.0p0 (2014-12-25 revision 49005) [x86_64-darwin13] $ gosh -V Gauche scheme shell, version 0.9.5_pre1 [utf-8,pthreads], x86_64-apple-darwin13.4.0 $ ocaml -version The OCaml toplevel, version 4.02.0 $ ghc -V The Glorious Glasgow Haskell Compilation System, version 7.8.3
enum型からその中の型へのキャスト
enum型からその中の型へのキャストする underlying_cast
を書いた。
#include <cinttypes> #include <type_traits> #include <array> #include <cstdio> template <class E> constexpr typename std::underlying_type<E>::type underlying_cast(E e) noexcept { return static_cast<typename std::underlying_type<E>::type>(e); } enum class Ki : std::uint8_t { momo = 3, kuri = 3, kaki = 8 }; template <class T> void p(T n) { std::puts("something"); } template <> void p(std::uint8_t n) { std::printf("uint8_t %" PRIu8 "\n", n); } int main() { p(underlying_cast(Ki::kaki)); std::array<int, underlying_cast(Ki::kaki)> a{}; return 0; }
$ clang++ -std=c++11 -o underlying_cast underlying_cast.cpp $ ./underlying_cast uint8_t 8
やったぜ。
Cプリプロセッサマクロ挙動クイズ
#define STR(a) #a #define STR_(a) STR(a) #define CONCAT(a,b) a##b #define CONCAT_(a,b) CONCAT(a,b) #define M1(a) STR(CONCAT(a, __LINE__)) #define M2(a) STR_(CONCAT(a, __LINE__)) #define M3(a) STR(CONCAT_(a, __LINE__)) #define M4(a) STR_(CONCAT_(a, __LINE__)) int main() { puts(M1(a)); puts(M2(b)); puts(M3(c)); puts(M4(d)); }
以上のような foo.c
があるとして、
$ clang -E foo.c | sed '/^#/d'
するとどうなるか。つまり、プリプロセッサを通すとどうなるか。
続きを読むmethod-missingを作ってみた
勢い余って「適用できるメソッドがなければ method-missing を呼び出す」機能を作ってみた.
(define-generic method-missing) (define-class <method-missing-generic> (<generic>) ()) (define-method apply-methods ((gf <method-missing-generic>) methods args) (if (null? methods) (apply method-missing gf args) (next-method)))
ドキュメントがなくて推測だけど,apply-methods
は(sort-applicable-methods
で)ソートされた適用可能なメソッドのリストを受け取り,next-method
が次のメソッドを呼び出すように設定して,メソッドを呼び出すのだろう.とにかく適用可能なメソッドがなくても apply-methods
は(空リストを伴って)呼ばれるようだ.
(define-generic foo :class <method-missing-generic>) (define-method foo ((num <number>)) `(number ,num)) (define-method method-missing ((gf <method-missing-generic>) (str <string>) :rest args) `(missing ,(~ gf 'name) string ,str)) (foo 42) ; => (number 42) (foo "bar") ; => (missing foo string "bar") (define-method foo ((num <string>)) `(string ,num)) (foo "bar") ; => (string "bar")
呼び出すジェネリックファンクション foo
は予め定義しておかないといけないので有り難みは薄い気がする.