2017年10月31日火曜日
2017年10月30日月曜日
手続き的なscanlの説明
手続き的なscanlの説明
foldの動作をxへの代入、再代入と捉えると分かりやすくなります。
直接foldの動きを見るのではなく、
まずscanlの動作を見てみます。
scanlの型は
Prelude> :t scanl
scanl :: (a -> b -> a) -> a -> [b] -> [a]
となっています。
scanlは、2引数の関数と初期値と何らかのリストを引数にとり、リストを返す関数です。
実際に動かしてみましょう。
Prelude> scanl(\x y -> x + y) 0 [1..3]
[0,1,3,6]
scanlが内部で何をやっているのか想像してみます。
まずscanlが呼び出されたとき、変数xに初期値、(この場合は「0」)を代入します。
次に変数yにリストの最初の要素(この場合は「1」)を代入します。
次にx+yを計算して結果の「1」をxに再代入します。
次にyにリストの次の要素「2」を代入し、x+yを計算します。この時xは1になっていますので, 結果は「3」です。これをxに再代入します。
次にyにリストの次の要素「3」を代入し、x+yを計算します。この時xは3になっていますので,結果は「6」です。これをxに再代入します。
リストの終わりに到達したので、リスト[0,1,3,6]を出力して終了します。
scanlはxに値が代入された時はすべてその値をリストに保存するようです。
こう見ますとfoldlはscanlと動作は同じで終了時にxの値のみを出力して終了する、と捉えることができます。
Prelude> :t foldl
foldl :: (a -> b -> a) -> a -> [b] -> a
foldlの型です。
同様にscanrの動作もみてみます。
scanrはxとyの役割が逆転しており、リストの作り方も異なっています。
Prelude> scanr (\x y -> x / y) 2 [3..5]
[1.875,1.6,2.5,2.0]
pythonで動きを追ってみると
>>> x=5.
>>> y=x/y
>>> y
2.5
>>> x=4
>>> y=x/y
>>> y
1.6
>>> x=3
>>> y=x/y
>>> y
1.875
代入、再代入はyに対しておこなわれます。
yに代入があった時、リストの先頭に値を保存します。
Prelude> 1.875:1.6:2.5:2.0:[]
[1.875,1.6,2.5,2.0]
haskellはソースコード上での、変数への値の再代入を許していません。
そのため繰り返しのための構文が用意されていません。
それが可能な言語、例えばPythonでは次のようなコードが書けます。
x=0
for y in [1,2,3]:
x=x+y
print x
これは上記の説明のfoldと全く同じです。
つまりfoldは一般的な言語の繰り返し処理をHaskellで書くための一つの方法になっているわけです。
(PythonのFor文は他の凡百の言語のFor文とはまったく異なります。どちらかというとMapに近い。ただ書き換えのできるグローバル変数も使えるので非常に自由度が高い。)
fold を手続き的にとらえると考えやすくなる処理があります。
Data.List モジュールには insert という関数が用意されています。
Prelude Data.List> :t insert
insert :: Ord a => a -> [a] -> [a]
ソート済みのリストに一つ要素を加えてソート順を崩さずに新たなリストを返す関数です。
他の関数が一般的なリスト処理の関数なのに比べてやや特殊な感じがあります。
たぶん「挿入ソートを実装してください」というメッセージだと思います。
挿入ソートは再起でも書けるとは思うのですが、いきなり fold を使ったほうがわかりやすい。
Prelude Data.List> foldl (\x y -> insert y x) [] [2,7,4,0]
[0,2,4,7]
まず初期値は空リストです。
x に 代入されます。
y には リストの第一要素が代入されます。
x y をひっくり返して insert 関数を適用します。
結果 x に [2] が y に 7 が代入されます。
7 を [2] に insert し [2,7] ・・・・(略)
この操作をリストの終わりまで繰り返します。 これでソート完了。
flip を使って書き直すと
Prelude Data.List> foldl (flip insert) [] [2,7,4,0]
[0,2,4,7]
さらに
Prelude Data.List> foldr insert [] [2,7,4,0]
[0,2,4,7]
foldl と foldr では動作が異なりますが、この場合は結果は同じです。
も参照ください。
フィボナッチ数
f6 の実行例
*Main> last $ take 10 f6
[55,34,21,13,8,5,3,2,1,1,0]
これは
foldl (\x@(a:b:_) _ -> (a+b):x) [1,0] [0..8]
と同じです。
フィボナッチ数列の隣り合う 2 項の比は黄金比に収束Wします。
$ runghc fibo2.hs | runghc gr.hs
~
~
~
1.618033988749895
1.618033988749895
1.618033988749895
~
~
~
Ctrl-Cを押して終了。
2017年10月28日土曜日
シェルピンスキーのギャスケット
まずパスカルの三角形をつくります。
$ runghc -XParallelListComp pascal.hs 3
[1]
[1,1]
[1,2,1]
[1,3,3,1]
[1,4,6,4,1]
[1,5,10,10,5,1]
[1,6,15,20,15,6,1]
[1,7,21,35,35,21,7,1]
奇数部を1、偶数部を0に置き換えます。
$ runghc -XParallelListComp sierpinski.hs 10 | display
描画には10数秒かかります。
コンパイルすればほぼ一瞬で描画するようになります。
http://senjounosantafe.blogspot.com/2019/02/blog-post.html
上記のページに
Python のコードを出力し
Turtle グラフィックスで
シェルピンスキーのギャスケットを再帰的に描画するHaskellのコードを置いています。
また1次元のセル・オートマトンにルール90を適用することでもシェルピンスキーのギャスケットを描画できるそうです。
実行例
$ runghc ca.hs
main を
main = loop0 fs 31 $ reverse "11100001"
と書き換えるとルール225を描画します。
Intから2進数の文字列に変換するには
「2進、8進、10進、16進の各表現を相互に変換する」のページ
https://github.com/haskell-jp/recipe-collection/blob/master/%E6%95%B0%E5%80%A4/2%E9%80%B2%E3%80%818%E9%80%B2%E3%80%8110%E9%80%B2%E3%80%8116%E9%80%B2%E3%81%AE%E5%90%84%E8%A1%A8%E7%8F%BE%E3%82%92%E7%9B%B8%E4%BA%92%E3%81%AB%E5%A4%89%E6%8F%9B%E3%81%99%E3%82%8B.md
を参考に。
具体的には
ghci> import Numeric
ghci> import Data.Char
ghci> showIntAtBase 2 intToDigit 123 "" -- 123を2進数表記
"1111011"
ただし8桁になるように先頭にゼロを埋めること。
"1111011" -> "01111011"
参考ページ
Elementary Cellular Automata
http://atlas.wolfram.com/01/01/
その他のルールも適用可能にし精細な画像を描くにはこちら。
http://hhg2the-haskell.blogspot.com/2019/02/blog-post.html
マンデルブロ集合
pbmファイルに書き出してマンデルブロ集合を描きます。(画像サイズは1000x1000に固定しています。)
コンパイルします。
$ ghc -O2 -threaded -rtsopts --make -XFlexibleContexts -eventlog mandel.hs
$ ./mande -2 2 2 3000 +RTS -N2 -l | display
displayコマンドにパイプします。
動作を確認。
$ threadscope mamdel.eventlog
殆どの時間2Coreで動いています。
Data.Array.Repa.IO.BMP
モジュールには
readImageFromBMP :: FilePath -> IO (Either Error (Array U DIM2 (Word8, Word8, Word8)))
writeImageToBMP :: FilePath -> Array U DIM2 (Word8, Word8, Word8) -> IO ()
という関数がありBMPファイルの読み書きができる。
画像サイズもコマンドラインから与えます。
$ ./mandel2 -2 2 2 1000 3000 +RTS -N2 -l
1次元の配列に対し map し、 reshape すればよいので、コード自体はわかりやすい。
index のTable (Int のタプルの配列)を作るのに時間がかかっているようなので、
配列を連結するやり方も試みた。
また mandel2.hsにおいてIndexをIntではなくWord16に置き換えてみたが効果は微妙だった。
世の中にはGPUを使ってマンデルブロー集合を極めて高速に描画するプログラムもあるらしい。
コンパイルします。
$ ghc -O2 -threaded -rtsopts --make -XFlexibleContexts -eventlog mandel.hs
$ ./mande -2 2 2 3000 +RTS -N2 -l | display
displayコマンドにパイプします。
動作を確認。
$ threadscope mamdel.eventlog
殆どの時間2Coreで動いています。
Data.Array.Repa.IO.BMP
モジュールには
readImageFromBMP :: FilePath -> IO (Either Error (Array U DIM2 (Word8, Word8, Word8)))
writeImageToBMP :: FilePath -> Array U DIM2 (Word8, Word8, Word8) -> IO ()
という関数がありBMPファイルの読み書きができる。
画像サイズもコマンドラインから与えます。
$ ./mandel2 -2 2 2 1000 3000 +RTS -N2 -l
index のTable (Int のタプルの配列)を作るのに時間がかかっているようなので、
配列を連結するやり方も試みた。
また mandel2.hsにおいてIndexをIntではなくWord16に置き換えてみたが効果は微妙だった。
世の中にはGPUを使ってマンデルブロー集合を極めて高速に描画するプログラムもあるらしい。
2017年10月27日金曜日
画像ビューアー
この画像(IMGP2850.JPG)を表示します。
ただこのやり方だと元画像の大きさそのままで表示されます。
サイズを変更して表示するには一旦Pixbufのインスタンスをつくってから
Buttonに貼り付けます。
かんたんな画像のビューアーをつくります。 まずディレクトリーを選択するダイアログ。
dirというファイルにリダイレクトしておきます。
$ runghc dirchooser.hs > dir
ビューアー
ただ表示するだけではあまりにも芸がないのでちょっとした操作をしてみます。
一括処理を選択します。
画像の回転指定
確認
MyTable モジュール
最後にConvertコマンドを実行します。
スクリプトは
runghc dirChooser.hs > dir
runghc select.hs > tmp
cat tmp | runghc image.hs > tmp1
cat tmp1 | runghc confirm.hs > tmp2
cat tmp2 | runghc mkCom.hs
gtk2hs
gtk2hsのインストールは
sudo apt install libghc-gtk-dev
先にGHCを単独でインストールしてしまうとgtk2hsを加えるときに非常に時間がかかります。
Gtk2hsを使う予定がなくてもいきなり入れてしまったほうが良い。
もちろんこれでGHCも使えるようになります。
Gtk2hsを使う予定がなくてもいきなり入れてしまったほうが良い。
もちろんこれでGHCも使えるようになります。
このコードを実行してウィンドウが表示されればインストールは成功です。
登録:
投稿 (Atom)
myPlayer
-- pipe.hs import System.Process import System.Environment main :: IO () a:_ IO [FilePath] randomize lst = do let c = length lst ...
-
この画像(IMGP2850.JPG)を表示します。 ただこのやり方だと元画像の大きさそのままで表示されます。 サイズを変更して表示するには一旦Pixbufのインスタンスをつくってから Buttonに貼り付けます。 かんたんな画像のビューアーをつくり...
-
上のコードをコピペして実行する。 $ runghc a.hs a.hs:3:11: error: Variable not in scope: n たぶんこのようなエラーになるはずです。 最初はなぜエラーになるのか理解できませんでした。 Vim上ではまったく...
-
pbmファイルでシェルピンスキーのギャスケットを描きます。 まずパスカルの三角形をつくります。 $ runghc -XParallelListComp pascal.hs 3 [1] [1,1] [1,2,1] [1,3,3,1] [1,4,6,4,1...