地味に復活
この記事( https://pc.watch.impress.co.jp/docs/news/1156297.html )を読んでから、プログラミング格差が広がりそうという危機感から小学生向けのHaskell本を執筆中。
PC買えない低所得者層の子供とか、学校でしかPC触れない可能性あるし、離島の子供は台風や火山の噴火で停電したら1週間くらいPC触れない可能性があるし、自治体によっては必要な機材が揃えられないかも知れないので、紙と鉛筆があれば出来るHaskellプログラミングと言う…。
とにかく必要最小限の知識でプログラミングを教える。
一度完成させた後、小学生向けの砕けた口調や絵を加える形の予定。
目的。
プログラミング必修化での地域格差是正。
算数の形式化への苦言。
(どうしてそうなるのかの理解がプログラミングの論理的思考の育成に重要)
■
自作ライブラリとそれを使った成果物。
LLだとここまでライブラリ化するとreverse使わない場面でもメモリに溜め込みまくったり、それを解消するためにイテレータ作りまくったりする必要があるんじゃなかろうか。
それでなくとも副作用と純粋部分が分かれてない処理があるとライブラリ化しにくい。
rpコマンドはreplaceとかrepと付けたかったけど、sudo apt installしてないパッケージに同名のコマンドが複数あるらしいので。
rpコマンド、なるべくユニークな文字列にしないと危険だけど、関数名や引数の名前や順序変えた時に複数ファイル・複数箇所を修正するので地味に大活躍…。
Myfunc.hs(自作ライブラリ) module Myfunc where import Data.List import Text.Printf consnum::(Int,String) -> String consnum (i,xs) = printf "%4d:%s" i xs fline f = unlines.f.lines fnumbering f = fline ((map consnum).(zip [1..]).f) redstr::String -> String redstr [] = [] redstr w = printf "\ESC[1m\ESC[31m%s\ESC[39m\ESC[0m" w bluestr::String -> String bluestr [] = [] bluestr w = printf "\ESC[34m%s\ESC[39m" w grep w = fline (filter (isInfixOf w)) replace _ _ [] = [] replace [] _ cs = cs replace w nw cs | w == take (length w) cs = nw ++ replace w nw (drop (length w) cs) replace w nw (c:cs) = c:replace w nw cs putfc (f,c) = printf "%s\n%s" f c writefc (f,c) = writeFile f c mfptn fs f ofs output = mapM readFile fs >>= return.(zip ofs).map f >>= mapM_ output mfput f fs = mfptn fs f (map bluestr fs) putfc mfwrite f fs = let tfs = map (++ ".temp") fs in mfptn fs f tfs writefc >> mfptn tfs id fs writefc number.hs(ナンバリングコマンド) import System.Environment import Myfunc main = getArgs >>= mfput (fnumbering id) revnumber.hs(行番号付き行の逆順表示コマンド) import System.Environment import Myfunc main = getArgs >>= mfput (fnumbering reverse) mygrep.hs(検索文字列のある行を表示するコマンド。見つかった文字列を強調赤字で表示) import System.Environment import Myfunc main = getArgs >>= \(w:fs) -> mfput ((replace w (redstr w)).(grep w).fnumbering id) fs rp.hs(文字列置換コマンド) import System.Environment import Myfunc main = getArgs >>= \(w:nw:fs) -> mfwrite (replace w nw) fs
ネットで1000万行のファイルを読み込ませて逆順にしたいとか言うのを(perlで)みたので、Cで1500万行近くまで対応してみた。
やり方としては一回EOFまでfputs回して、ftellで位置情報を配列に記録。
配列の逆から位置情報読み込んでfseekで移動を繰り返す。
記録時間が最初かかるけど、配列の大きさからビビっていたほどはメモリも消費しない。
(1400万行3.1GBの文字ファイルに対し、4GBメモリの0.6%)
#include<stdio.h> #include<stdlib.h> int main(int argc, char *argv[]) { for(int i = 1; i < argc; i++) { FILE *fp; if((fp = fopen(argv[i],"r"))) { const int BUF = 1024 * 4; char s[BUF]; long *fpos; int k = 0; puts(argv[i]); fpos = malloc(sizeof(long)*15000000); if (fpos == NULL) exit(0); fpos[++k] = ftell(fp); while((fgets(s,BUF - 1, fp))) { fpos[++k] = ftell(fp); } int j = 1; fseek(fp,fpos[--k],SEEK_SET); while(fgets(s,BUF - 1,fp)) { if(k > 0) { printf("%4d:%s", j++, s); fseek(fp,fpos[--k],SEEK_SET); } } free(fpos); fclose(fp); } else { printf("ファイル[%s]がありません\n",argv[i]); return -1; } } return 0; }
自分なりの宣言的プログラミングの定義
関数型言語や論理型言語(Prolog)は宣言的にプログラミングが出来ると入門書には書いてることがあります。
一方で、Javaの本でも宣言的なコードと書かれたものがあります。
どうもメソッド呼び出しのみが並ぶようなコードっぽい。
となると、ifやfor等の制御構造が無ければ宣言的と言う事なのかな?
まあ、手続き型言語の関数やオブジェクト指向も、もとは抽象的に扱うための物ですしね。
宣言的であるのと、参照透明な事は関係ないと考えればしっくりきます。
昔、2chで型がある言語で書く人はクラスをよく書くけど、型のない言語で書く人はクラスをあまり書かないとか見かけましたが、ある意味ではクラスを書かなくて済むくらいになるのが手続き型言語の抽象化の理想ではないかと。
手続き的な処理は一切書かなくて良くて、全てメソッドや関数の呼び出しだけで完結できるようになったら、関数型言語と同じくらいの抽象度になっているのかもしれません。
そう言えば、次期JavaScriptが末尾再帰最適化出来るようになるそうですね。
理屈の上では手続き型言語だって出来ると思ってましたので、他の手続き型言語にも採用されていくと思います。
しかし、Haskellはいつの間にか普通の(単純な)再帰も(恐らく内部で末尾再帰に変換して)ループになるようになってました。
これは地味にうれしい。
これも、いずれは手続き型言語に採用されていくように思いますが、もう少し先でしょう。
C#の型推論もまだまだですね。
そして、あれ以上の推論は手続き型では無理な気はしますね。
手続き型で関数型言語並みの抽象度になるのは結局LLやsmalltalkの様な型の無い言語だけのように思います。
だったら、型があって抽象度の高いHaskellが(ry
Haskellで九九
手続き型言語では、オブジェクト指向全盛になった今もついループで書いちゃう九九。
じゃあ、ループじゃない書き方ってどんなだろう?と思ったので書いてみた。
import Text.Printf kuku =putStr $ concat [nineLines x y | x <- [1..9], y <- [1..9]] where nineLines :: Int -> Int -> String nineLines x 9= printf "%2d\n" (x * 9) nineLines x y= printf "%2d " (x * y)
うん。普通にLLでも書けますね。
でも、何故か書かない。
そんなコードでした。
HaskellのIOモナドは副作用は有るかも知れないけど、参照透明性は失われてないと思う
すごいH本を読み終えての感想。
IOモナド、あるいはアクションと呼ばれるものは、Haskellの中ではリストやMybeと同列。
ただIO型はモナドのインスタンスにしかなってないだけ。
(まあEqもOrdも出来ないからインスタンスになれないよね…)
なのでリストの中にputStrとかを入れたりできる。
IO型の関数はモナドで逐次処理をエミュレートしながらも、遅延評価のおかげで参照透明性は失われてないと思う。
エミュレートは所詮エミュレートだと言う事。
例えば、
文字を入力して出力するプログラム。
main = do x <- getContents putStr x
一見、putStr xから先に実行すると破綻しそうだが、遅延評価なのでputStrが必要になったらgetContentsを呼びに行く。
だから入力→出力の順で実行される。
(というか、実際そんな順序で実行されてる)
もちろん、コード通りにgetContentsから実行されれば普通の逐次処理。
ちなみに、ループが書かれてないけどこれは入力と出力を永遠に続ける無限ループします。
というのは、ファイルの終端まで入力してから出力するはずが、標準入力にはファイルの終端が無いため、遅延評価により一行毎(入力確定毎)に出力を繰り返します。
作ったプログラムにリダイレクトでファイルを入力するようにすると、期待通りファイル内容を表示して終了します。
次に、入力だけのプログラム。
main = do x <- getContents
これはdo表記を使わないとこうなる。
main = getContents >>= \x -> x
つまり、xに値が入ってから入力が開始されるのはおかしい。
では、Haskellはどう解決するのか?
遅延評価は求められない限り何もしない。
つまり、出力しない値は無いも同じなので、入力も何もせずに終了する。
ほらね?
参照透明性は失われてないでしょ?
すごいH本のファンクターまで読んだときに思ったのは、IO型はプログラム型とかアプリ型とか呼びたくなるって事。
リストやMybeとかと同じ値を入れる型だけど、すごいH本で言う文章付き型(複数の値を入れる型や、失敗の可能性のある型)とすれば、プログラムとして動作する型とでも言おうか…。
型としてのプログラム型は同じ形の入れ物。(だから、Haskellの文法上副作用はない)
プログラム型がプログラムとして実行されると副作用を伴う的な。
済みません。
まだ頭の中で整理できてない。
熊本へ支援物資届けた時に感じたことと、ボランティア心得的なまとめ
まさかまたはてなダイアリーを使う日が来ようとはね。。。
熊本へ支援物資届けた後、つらつらとツイートしたのをここにまとめます。
今回の熊本の地震では陸路による物流の重要性と、その脆さを実感した。
— しんちゃん (@n_shinchan) 2016年4月18日
もっとヘリで運ぶとか出来ないのかな。。。
集積場には支援物資届いてるらしいのに、そこから先が手探りなのがね。。。
多分、まだ見つかって無い避難所とか一杯ある。
まさに震源地って地域で地震速報を受け取る恐怖。
— しんちゃん (@n_shinchan) 2016年4月18日
余震でも怖い。
被災地の人達は毎日これ聴いてると思うと、気が狂いそうだろうな。。。
でも、それが命綱にもなるから皮肉なもの。
鹿児島事情で言えば、温泉水は無限にあるけど、それを運ぶポリタンクが売り切れた。
— しんちゃん (@n_shinchan) 2016年4月18日
深刻なポリタンク不足。
被災者の人も、変に遠慮せずに空のポリタンクをどんどん返して欲しい。
— しんちゃん (@n_shinchan) 2016年4月18日
じゃないと温泉水が汲めない。
ボランティア支援物資は昼間を避けたほうが良い。
— しんちゃん (@n_shinchan) 2016年4月18日
本来の自衛隊とかの輸送が大渋滞で遅れる。
避難所は一つじゃない。
— しんちゃん (@n_shinchan) 2016年4月18日
毎回違うルートを通る中、途中で野宿してるのや、別の避難所も見かけた。
見送らないといけないのが辛かった。。。
中途半端な量の支援物資はかえって邪魔。
— しんちゃん (@n_shinchan) 2016年4月18日
200人のところに20人分しか持ってこないとかだと微妙な空気。。。
軽トラックとかである程度の量。
もしくは複数人数で物量を確保したほうが喜ばれる。
踏み外したら車棄てないといけない道も通る勇気が必要。
— しんちゃん (@n_shinchan) 2016年4月18日
震源地付近はこんな道ばかり。
段差の有る橋や道とか車の下を擦る擦る。 pic.twitter.com/cFJaGO9kWT
九州電力も、平気だからと川内原発動かし続けずに、停止して他の電力へ切り替えてほしかった。
— しんちゃん (@n_shinchan) 2016年4月18日
無駄に不安煽ってる。
と言うか、道路ズダズダなのに避難経路もくそもない。
同じルートは通れないと思え。
— しんちゃん (@n_shinchan) 2016年4月18日
カーナビが指示するルートは通行止めが多い。
しかも日に日に通行止めが増える。
教訓的には少しでも新鮮な状態でおにぎり渡したいからって、行きすがらに食糧買うのはダメ。
— しんちゃん (@n_shinchan) 2016年4月18日
物流が震災の影響受けてるから、被災地だけでなく、近隣の県も物資不足だから食べ物に困る人が増える。
昨日、印象に残ったことをツイート。
— しんちゃん (@n_shinchan) 2016年4月18日
朝の炊き出しのお米が無い自衛官が上官に電話で泣き付いてた一言。
「何も無いのに大行列できてるんですよ!?俺たちにどうしろって言うんですか!!」
支援物資が届かなかったら、自衛隊はただの穀つぶし。
東日本大震災の教訓はどうした。。。