チューリング不完全

What are you afraid of? All you have to do is try.

Fabricで多段ssh

Fabricで多段sshした上でコマンド実行させることができたので、書いておきます。

  • 環境
    • Python 2.6.6
    • Fabric 1.5.1 (paramiko 1.9.0, ssh 1.8.0)
    • サーバは全てCentOS 6.3

やり方は.ssh/configに書かれたProxyCommand設定を読み込む方法とFabricのenvをいじる方法の2つがあります。
ここではFabricのenvをいじる方法について解説します。

環境

+---------------+
|192.168.200.99 |
+---------------+
       ↓
+---------------+
|192.168.200.100|
+---------------+
       ↓
+---------------+
|192.168.200.101|
+---------------+
       ↓
+---------------+
|192.168.200.102|
+---------------+
  • 192.168.200.99

プログラムを動かすところ。

  • 192.168.200.100

踏み台役。アクセス制限や鍵などの設定は何もしていない。

  • 192.168.200.101

TCP Wrapperで、sshでの接続は192.168.200.100からのみに制限している。

  • 192.168.200.102

TCP Wrapperで、sshでの接続は192.168.200.101からのみに制限している。


コード

# fabric_test.py
from fabric.api import run, env
from fabric.state import connections, output
from fabric.network import denormalize
from fabric.exceptions import NetworkError

via = [('user1@192.168.200.100:22', 'password1'),
       ('user2@192.168.200.101:22'. 'password2'),
       ('user3@192.168.200.102:22', 'password3')]

try:
    for host, passwd in via:
        env.gateway = env.host_string
        env.host_string = host
        env.password = passwd
        run('', quiet=True)
    run('hostname')    

except NetworkError as e:
    print(e)
finally:
    for key in connections.keys():
        if output.status:
            print("Disconnection from %s" $ denormalize(key))
            connections[key].close()

実行結果

% python fabric_test.py
[user3@192.168.200.102] run: hostname
[user3@192.168.200.102] out: server3
[user3@192.168.200.102] out:

Disconnection from user3@192.168.200.102...
Disconnection from user2@192.168.200.101...
Disconnection from user1@192.168.200.100...

鍵情報が必要な場合は、env.key_filenameにファイルパスを渡してあげるといいです。
(ただし、ファイルパスは当然プログラムが動いている環境のパスになります。)
基本的にはtryの部分の処理のみでOKなのですが、プログラムが終了するときにconnectionをcloseしてあげないと、以下の様なエラーが飛んできます。

Exception in thread Thread-2 (most likely raised during interpreter shutdown):
Traceback (most recent call last):
  File "/usr/lib64/python2.6/threading.py", line 532, in __bootstrap_inner
  File "/usr/lib/python2.6/site-packages/paramiko/transport.py", line 1613, in run
<type 'exceptions.AttributeError'>: 'NoneType' object has no attribute 'error'
Exception in thread Thread-3 (most likely raised during interpreter shutdown):
Traceback (most recent call last):
  File "/usr/lib64/python2.6/threading.py", line 532, in __bootstrap_inner
  File "/usr/lib/python2.6/site-packages/paramiko/transport.py", line 1613, in run
<type 'exceptions.AttributeError'>: 'NoneType' object has no attribute 'error'



初めはFabric1.4.3の状態で「うーん、できない・・・」と悩んでいたのですが、Fabric1.5でenv.gatewayと.ssh/configのProxyCommandを読み込む機能が追加されたのを知り、上記のような感じになりました。

追記(2014/10/07)

上記コードではFabricの現行のバージョンで動きませんので、修正しました。
Fabricの1.6.4/1.7.5/1.8.5/1.9.1/1.10.0で動作確認済みです。
1.5.5では動きませんでした。

# fabric_test.py
from fabric.api import run, env
from fabric.state import connections, output
from fabric.network import denormalize
from fabric.exceptions import NetworkError

via = [('user1@192.168.200.100:22', 'password1'),
       ('user2@192.168.200.101:22'. 'password2'),
       ('user3@192.168.200.102:22', 'password3')]

try:
    for host, passwd in via:
        env.gateway = env.host_string
        env.host_string = host
        env.password = passwd
        env.passwords[host] = passwd
        connections.connect(host)
    run('hostname')    
except NetworkError as e:
    print(e)
finally:
    for key in connections.keys():
        if output.status:
            print("Disconnection from %s" % denormalize(key))
            connections[key].close()

2012年夏のプログラミング・シンポジウムに参加してきました

2012年夏のプログラミング・シンポジウム

f:id:aomori-ringo2:20120825182623j:plain


会場の様子

参加人数約160人と大変盛況でした。会場の後ろにはオーム社オライリー・ジャパン,翔泳社という、このようなイベントに来る人がいかにも好きそうな出版社が書籍販売をしていました。(私も大好きです。)


f:id:aomori-ringo2:20120828154035j:plain
f:id:aomori-ringo2:20120828153944j:plain


オライリーさんは消費税なし、10%off、5000円以上購入でオライリーTシャツプレゼントというキャンペーンを行なっていました。私はTシャツが欲しかったので2冊何か買おうとしたら、定価だと2冊で5500円なのに1割引きのせいで4950円になってしまう、など非道とも言える組み合わせが存在し、みなさん苦悩していました。オライリーの人はニヤニヤしてました。
悩んだ結果、私は「Real World Haskell」と「リーダブルコード」を購入。Tシャツをバッチリいただきました。

プログラミング美学(仮)/ 竹内郁雄(早稲田大学)

(WIP)

Beautiful Programming Language and Beautiful Testing / 山本和彦(IIJ-II)


Haskellerはあまりテストを書かないけど、Haskellにも当然テストは必要だから、テストを書かせるようにしよう、という話。
Haskeller向けではない人向けと書いてはいましたが、最終的にはHaskellのコードでの説明になってしまったのは致し方無いですかね・・・


午前中のここまでの3つの発表で登場した言語がLisp, Haskell, Haskellと、関数型言語を触ったことがない参加者には随分と酷な並びだったような。



ビューティフルコードのためのN個の指針 / 久野靖(筑波大)

オライリーの「Beautiful Code」の訳者である久野先生による、コードの綺麗さに対するお話。(WIP)

Ruby を用いた超絶技巧プログラミング / 遠藤侑介


この日で一番盛り上がった発表。
アスキーコードのように見える自己複製プログラム」「小文字アルファベットと空白のみでHello world」など、トンデモプログラム満載の発表。デモをする度に会場は笑いと拍手が巻き起こっていました。
「実用性なんかなくてもプログラミングは楽しい」という導入部の主張がとても素晴らしいと思いました。



高機能アセンブラによるx86/x64 CPU向け高速化テクニック / 光成滋生(サイボウズ・ラボ株式会社)

(WIP)

ビスケットにおけるプログラムの美しさについて / 原田康徳(NTT)

(WIP)


キュート・アルゴリズム / 稲葉一浩


Redcoderである@kinabaさんによる、アルゴリズムのお話。

  • 『最悪』のソートアルゴリズムは何か
  • 非破壊型のQueueを実装する
  • 正確な実数を計算する

私も一時期Topcoderにはまっていたのでとても楽しめました。競技プログラミング経験がある人はスライドをご覧になってみると良いと思います。


セルオートマトンのプログラムハック / 和田英一(IIJ-II)


個人的には今日一番難しかった発表。和田先生が今までに出会ってきたびっくりするようなプログラムの数々の中から、セルオートマトンに焦点をしぼっていくつか紹介されていました。
スライドを見てもよくわからないかも知れませんが、やりたいことをとにかく最速(or最小)で行うために限界まで意味をばらしてコードに再構築をしているものが多く、プログラマは数学ができた方が格段に強いな、と感じました。
このようなお話はクヌース先生のTAOCPやHacker's Delightに数多くあるようなので、ご興味がある方は読んでみるといいでしょう。
発表の最後で和田先生はこのように語っていました。



肝に命じたいですね。



まとめ

私は常々、今後数十年、ずっとプログラミングやソフトウェア開発に従事してご飯を食べていくことができるだろうか・・・と不安を抱きつつ勉強をしています。今回は最先端で活躍している方の発表を聞いて、全く意味がわからなくてお手上げになるということもなく、どの発表も楽しめたので、少し安心しました。
和田先生によると、プログラミングシンポジウムの夏は来年も今回のように日帰り・参加費無料で行うつもり、とのこと。来年もぜひ参加したいですね。

第3回 スタートHaskell2に参加してきました

第3回 スタートHaskell2 - [PARTAKE]

f:id:aomori-ringo2:20120722181333j:plain
今回はがんばったぞ!


すごいHaskellたのしく学ぼう!

すごいHaskellたのしく学ぼう!


すごいH本、

  • 第6章 モジュール
  • 第7章 型や型クラスを自分で作ろう

をやりました。



第6章 モジュール(@aomoriringo)

わたし ^o^ です

6章は「モジュール」と銘打っておきながら、大半が標準モジュールの解説に当てられているので、ストーリーがつけにくくて苦労しました。
モジュールのインポート、エクスポートについては書いてあることをwebでいろいろ調べていくうちにいろんなことにぶちあたって、これまた発表にどこまで含めるか決めるのに苦労しました。
結果的に本には書いてない部分についてもちょっと話したのですが、そしたら@kazu_yamamotoさんあたりからためになる話も聞けたし、よかったんじゃないかと思います。
標準モジュールのあたりは随分ぐだぐだしてしまったと反省しているのですが、終わったあとに@seizans先生に慰めてもらったのでよいことにします。




発表の内容はスライドを見てもらうとして、その後の質疑応答では以下の様な話題がありました。

  • import Map (Map), import qualified Map hiding (Map) as Mのように、同じモジュールを複数個にわけてimportすることができる。
  • import Foo ()みたいな書き方をした場合、インスタンス宣言だけがimportされる。
  • Hoogleは標準モジュールだけしか見れないが、Hayoo!だと標準モジュール以外も検索できる。(Hoogleにもドキュメントを追加できるけど、超絶めんどくさいらしい)
  • HaskellのCharは4バイトで、実はとてもリッチなデータ構造らしい。
  • foldl'はスライド中では「foldlと違ってスタックオーバーフローしない」と書いたが、foldl'の第1引数の関数が遅延評価するときは、スタックオーバーフローになることがある。
  • module構文でエクスポートする関数は明記したほうが良い(推奨されている)。書くとGHCが書かない時よりも最適化をがんばってくれる。
  • Mainモジュールのみモジュール宣言の名前はファイル名と一致しなくてもよい。(Mainを複数個作って使い分けたりできる)
  • モジュール名を省略した場合はMainモジュールと見なされる。
  • import文が多いコードは分割してimport文を減らした方が良さげ。

第7章 型や型クラスを自分で作ろう(@a_hisameさん)

  from a-hisame


正直7章は自分の発表が終わったのでとにかく安心しきって、話半分に聞いていました(ごめんなさい!)。
この部分については事前に読んできていたので、確認に近かったです。それにしても7章はとてもボリュームがあって@a_hisameさんすごいなーと思っていました。
6章は7章に比べてボリューム少なめだから、30~40分で切り上げて7章で時間を使ってもらいたいな・・・と思っていたのに、思い切り1時間使ってしまいました(´・ω:;.:...





LT | 再帰の俯瞰図(@kazu_yamamotoさん)

http://mew.org/~kazu/material/2012-recursion.pdf

すごいH本だと4章が再帰の話なのですが、あれではちょっと不十分だろうということで、末尾再帰や末尾再帰で書けないパターンの再帰などについての話でした。
このあとの演習問題では再帰をひたすら実装する課題があったのですが、再帰自体は書けるけど末尾再帰で書こうとするととても難しくて大変でした・・・もっと精進しなければいけません。





感想

プレゼン作成作業が思ったより難航してしまって、前日は朝6時までモンスターエナジーを飲みつつ作っていました。(あとで@a_hisameさんと話したらとても似た状態だったらしい)
自身の発表内容は反省点の多いものでしたが、本を1章ずつ担当するというスタイルの勉強会の発表者として、最低限の仕事はできたかなと思っています。いろいろ勉強になったし、やってよかった。
発表者のみなさま、主催者、運営者のみなさま、俺、お疲れ様でした。次回もぜひ参加したいと思います。

第2回 スタートHaskell2に参加してきました

第2回 スタートHaskell2 - [PARTAKE]

f:id:aomori-ringo2:20120722181333j:plain


最近趣味で触っていたHaskell、気になっていた勉強会についに今回はじめて参加することができました。



すごいHaskellたのしく学ぼう!

すごいHaskellたのしく学ぼう!


いわゆる「すごいH本」を教科書にしています。
今回は

  • 第3章 関数の構文
  • 第4章 Hello 再帰!
  • 第5章 高階関数

をやりました。



第3章 関数の構文(@mizu__tamaさん)

  • パターンマッチ
  • ガード
  • where
  • let式
  • case式

それぞれについては「すごいH本」を読めばわかることなので略します。(めんどい)
その他、以下の様な話題がありました。

上記にあげたものは全部case式の糖衣構文

whereとletの使いどころ




letとlet inの違い

「すごいH本」の第3章の中では、

4 * (let a = 9 in a + 1) + 2

のように基本的にはletはinセットで使用しているのですが、リスト内包表記とあわせて使用する場合は

calcBmis xs = [bmi | (w, h) <- xs, let bmi = w / h ^ 2]

のようにin句がない。
また、ghcで

let hoge = 100

のように書くと、以降ghci中でhogeを使用することができるようになります。
これについては@kazu_yamamotoさんが解説してくれていました。モナドがどうとか言っていた気がするのですが、きちんと理解はできなかったので既に脳内から雲散霧消してしまいました。

  • リスト内包表記では、リスト内包という表記の中で宣言するとスコープがその部分のみなのでwhereのようにリスト内包内部でのみ使用可能な変数みたいにして使える
  • ghc中でletを宣言すると、ghc全体のスコープに対して置かれるのでどこでも使えるようになる。(グローバル的な?)

というのが私の現時点での理解です。



第4章 Hello 再帰!(@ko1kunさん)

いろんな本からの参照があったのが印象的でした。(読んでみたい、と思った本もちらほら。)
内容としては再帰を使った初歩的な関数を書いてみよう、というものでした。このへんはプログラミングHaskellでもやっていたので大丈夫かな。
途中フィボナッチ関数の計算速度に関する話題がいろいろと出てきたのですが、Haskellとは関係のない、アルゴリズムの話だったように思います。






第5章 高階関数(@S1E11さん)

今日のヤマだったと思います。

  • カリー化
  • map, filter
  • ラムダ式
  • foldl, foldr
  • $を使った関数適用
  • 関数合成

foldl, foldrと無限リストが絡むあたりが難しく、つまりました。これは次回までに自分で悩んでおきたいところですね。

foldl, foldl', foldrの使い分け


関数合成をする際の.(ドット)、$について



その他にもTLでは正格とか末尾再帰の話題がありましたが、かなりわかっていないので今は見なかったことにしておきましょう。



LT | 文芸的プログラミング(@shokosさん)

hsファイルはコードが基本で、コメント部分は--や{- -}で指定するのですが、それが逆になり、基本がコメントで、コードについては明示的に指定するlhsについてのお話。
Birdモード, LaTeXモードの2つがあるようです。
参考: Executing Haskell program

文芸的プログラミング (ASCII SOFTWARE SCIENCE Programming Paradigm)

文芸的プログラミング (ASCII SOFTWARE SCIENCE Programming Paradigm)



LT | idのナゾ、constのヒミツ(@kazu_yamamotoさん)

haskellのid, constはどこで使うの?という話題がいつの間にかSKIコンビネータとかどんどんdeepな話に・・・
コンビネータについてやけに興奮した調子で話す@kazu_yamamotoさんが面白かったです。

数学パズル ものまね鳥をまねる POD版 ―愉快なパズルと結合子論理の夢の鳥物語

数学パズル ものまね鳥をまねる POD版 ―愉快なパズルと結合子論理の夢の鳥物語



感想

100人超で5時間超え、という規模の勉強会は初めてでしたが、発表だけでなくTwitterでのやりとりも楽しく、集中力を途切れさせずに最後まで楽しく参加できました。
発表者のみなさま、主催者、運営者のみなさま、お疲れ様でした。次回もぜひ参加したいと思います。

Aizu.LT::Tokyo #1に参加してきました

勉強会レポートを残していこうと思い立ったので、駄文ではありますが。


Aizu.LT::Tokyo #1 Partake
Aizu.LT::Tokyoという地名が2つも入ってもはやわけがわからないことになっている勉強会に参加してきました。
この勉強会は会津大学でライトニングトークをやろう!というコンセプトで@luxionが3年ほど前に立ち上げたもので、初期の参加メンバーがだんだんと東京で働くようになり、今回東京で第1回が催されることになりました。


今回のメニューは以下のとおり。

@luxion Groovy!
@scarletmoon0428 インターネットと著作権
@corosuke_k Corosuke meets UXD
@aomoriringo 最近読んだ本読みたい本


Aizu.LTは傾向として技術系と日常系の発表が半々ぐらいになるのですが、今回は(私のだけは微妙ですが)全て技術系よりの発表となりました。


当日のUstream録画
「あれ、LTって5分間なのでは・・・」とか思ってはいけません。Aizu.LTは「ルールがないのがルール」的な勉強会スタイルなんです。


時間こそ短かったですが、私としてはゆる~とした雰囲気で、久しぶりにプレゼンもできて楽しかったです。(内輪雰囲気すぎるのはなんとかしたいところですが・・・)
今回は日程の公開から勉強会までが1週間しかなかったため、参加者が5人しかいなかったのが悔やまれますね。次はもうちょっとがんばりましょう。なにか必要なことがあればお手伝いしますよ。 > 主催の@luxion


今回私が発表で紹介した書籍


アジャイルプラクティス 達人プログラマに学ぶ現場開発者の習慣

アジャイルプラクティス 達人プログラマに学ぶ現場開発者の習慣


7つの言語 7つの世界

7つの言語 7つの世界


プログラミングHaskell

プログラミングHaskell


オブジェクト指向入門 第2版 原則・コンセプト (IT Architect’Archive クラシックモダン・コンピューティング)

オブジェクト指向入門 第2版 原則・コンセプト (IT Architect’Archive クラシックモダン・コンピューティング)


新吼えろペン 1 (サンデーGXコミックス)

新吼えろペン 1 (サンデーGXコミックス)

プログラミングHaskell 第7章 高階関数

前章に比べるとレベル上がった印象。


import Data.Char(chr, ord, isLower, isUpper)

-- 7.1 基本概念
twice :: (a -> a) -> a -> a
twice f x = f (f x)

-- 7.2 リスト処理
map :: (a -> b) -> [a] -> [b]
map f xs = [f x | x <- xs]

map f [] = []
map f (x:xs) = f x : map f xs

filter :: (a -> Bool) -> [a] -> [a]
filter p xs = [x | x <- xs, p x]
filter p [] = []
filter p (x:xs) | p x       = x : filter p xs
                | otherwise = filter p xs

-- 整数のリストから偶数を取り出し、二乗した値の合計をとる
sumsqreven :: [Int] -> Int
sumsqreven ns = sum (map (^2) (filter even ns))

-- 7.3 畳込 foldr

sum = foldr (+) 0
product = foldr (*) 1
or = foldr (||) False
and = foldr (&&) True

foldr :: (a -> b -> b) -> b -> [a] -> b
foldr f v [] = v
foldr f v (x:xs) = f x (foldr f v xs)

length :: [a] -> Int
length [] = 0
length (_:xs) = 1 + length xs

length = foldr (\_ n -> 1+n) 0

reverse :: [a] -> [a]
reverse [] = []
reverse (x:xs) = reverse xs ++ [x]

-- 新しい要素をリストの末尾に追加する
snoc :: a -> [a] -> [a]
snoc x xs = xs ++ [x]

reverse [] = []
reverse (x:xs) = snoc x (reverse xs)

reverse foldr snoc []

-- error
-- (++ys) = foldr (:) ys 

-- 7.4 畳込関数 foldl
sum = foldl (+) 0
product = foldl (*) 1
or = foldl (||) False
and = foldl (&&) True

length = foldl (\n _ -> n+1) 0
reverse = foldl (\xs x -> x:xs) []
-- error
-- (xs++) = foldl (\ys y -> ys ++ [y]) xs

foldl :: (a -> b -> a) -> a -> [b] -> a
foldl f v [] = v
foldl f v (x:xs) = foldl f (f v x) xs


-- 7.5 関数合成演算子
(.) :: (b -> c) -> (a -> b) -> (a -> c)
f.g = \x -> f (g x)

odd n = not (even n)
odd = not.even

twice f x = f (f x)
twice f = f.f

sumsqreven ns = sum (map (^2) (filter even ns))
sumsqreven = sum.map (^2).filter even

id :: a -> a
id = \x -> x

compose :: [a -> a] -> (a -> a)
compose = foldr (.) id


-- 7.6 文字列の変換器
type Bit = Int

bin2int :: [Bit] -> Int
-- bin2int bits = sum [w*b | (w,b) <- zip weights bits]
--                where weights = iterate (*2) 1
bin2int = foldr (\x y -> x+2*y) 0

int2bin :: Int -> [Bit]
int2bin 0 = []
int2bin n = n `mod` 2 : int2bin (n `div` 2)

-- 二進表記を8ビットにする
make8 :: [Bit] -> [Bit]
make8 bits = take 8 (bits ++ repeat 0)

encode :: String -> [Bit]
encode = concat.map (make8.int2bin.ord)

-- 8ビットの二進表記に分割する
chop8 :: [Bit] -> [[Bit]]
chop8 [] = []
chop8 bits = take 8 bits : chop8 (drop 8 bits)

decode :: [Bit] -> String
decode = map (chr.bin2int).chop8

transmit :: String -> String
transmit = decode.channel.encode

channel :: [Bit] -> [Bit]
channel = id

exercise.

1.

リスト内包表記 [ f x | x <- xs, p x ] を
高階関数 map と filter を使って書き直すとどうなるか?

map f ( filter p xs )

2.

標準ライブラリでの定義を見ないで、
高階関数all, any, takeWhile, およびdropWhileを定義せよ。

myAll :: (a -> Bool) -> [a] -> Bool
myAll p [] = True
myAll p (x:xs) = (p x) && myAll p xs

myAny :: (a -> Bool) -> [a] -> Bool
myAny p [] = False
myAny p (x:xs) = (p x) || myAny p xs
  
myTakeWhile :: (a -> Bool) -> [a] -> [a]
myTakeWhile p [] = []
myTakeWhile p (x:xs) | p x       = x : myTakeWhile p xs
                     | otherwise = [] 

myDropWhile :: (a -> Bool) -> [a] -> [a]
myDropWhile p [] = []
myDropWhile p (x:xs) | p x       = myDropWhile p xs
                     | otherwise = x:xs


3.

関数foldrを用いて、関数map f と filter p を定義せよ。

ここでちょっと詰まりました。
map fは2つ定義が書いてあります。
まずはラムダ式の方を思いついたのですが、うーん?というのをTwitterでつぶやいたところ@osanai_先生に2つめのを教えてもらいました。
でもそれをfilterの方には全然いかせてません。

map :: (a -> b) -> [a] -> [b]
map f = foldr (\x y -> f x : y) [] 
map f = foldr ((:).f) []

filter :: (a -> Bool) -> [a] -> [a]
filter f = foldr (\x y -> if f x then x:y else y) []

4.

foldlを用いて、十進表記を整数に変換する関数
dec2int :: [Int] -> Int
を定義せよ。

dec2int :: [Int] -> Int
dec2int = foldr (\x y -> x + 10*y) 0


5.

以下の定義が誤りである理由を説明せよ。
sumsqreven = compose [sum, map (^2), filter even]

compose :: [a -> a] -> (a -> a)
map(^2), filter evenは同じ型だが、sumのみ異なるため。


6.

標準ライブラリの定義を見ないで、以下の2つの高階関数を定義せよ。
・「引数に組をとる関数」を「カリー化された関数」へ変換する関数 curry
・「引数が2つのカリー化された関数」を「引数に組をとる関数」へ変換する関数 uncurry
ヒント:まず、関数の型を書きなさい。

ここでどはまりしました。
引数を2つとる関数を定義する場合、まだどうしても左辺にf x y = みたいに書いてしまいます。

mycurry :: ((a, b) -> c) -> (a -> b -> c)
mycurry = (\a b c -> a (b,c))

myuncurry :: (a -> b -> c) -> ((a, b) -> c)
myuncurry = (\a (b,c) -> a b c)


7.

高階関数 unfold は、リストを生成する簡単な再帰の様式を閉じ込める。
この関数は、以下のように定義できる。


unfold p h t x | p x = []
| otherwise = h x : unfold p h t (t x)


すなわち、関数 unfold p h t は、
述語pを引数に適用した結果Trueとなれば、空リストを返す。
そうでなければ、関数hを引数へ適用することで先頭の要素を作り、
残りのリストは自分自身を呼び出すことで生成して、全体として空でないリストを作る。
再帰する際には、関数tを引数に適用して、新たな引数を作る。
たとえば関数unfoldを用いると、関数int2binはもっと簡潔に書き直せる。

int2bin = unfold (==0) (`mod`2) (`div`2)

関数 unfold を用いて関数 chop8, map f, iterate fを再定義せよ。
|

不完全です。というのもmapで適用できる型にEqクラスであるという制限がついてしまっています。
(==[])のせいなんですが、どうしたもんでしょうか・・・

unfold :: (a -> Bool) -> (a -> b) -> (a -> a) -> a -> [b]
unfold p h t x | p x = []
               | otherwise = h x : unfold p h t (t x)

chop8_unfold :: [Bit] -> [[Bit]]
chop8_unfold = unfold (==[]) (take 8) (drop 8)

map_unfold :: Eq a => (a -> b) -> [a] -> [b]
map_unfold f = unfold (==[]) (f.head) (tail)

8.

パリティービットを用いることで、
文字列を通信するプログラムを軽度の通信エラーを検出できるように変更せよ。
すなわち、8ビットの二進表記は、符号化の際にパリティービットを付加される。
パリティービットは、1の個数が奇数であれば1, そうでなければ0となる。
逆に複号の際は、9ビットの二進表記のパリティービットが正しいか検査する。
正しければパリティービットを捨て誤りであればパリティーエラーを報告する。
ヒント: ライブラリ関数 error :: String -> a は、
評価を強制終了し、与えられる文字列をエラーメッセージとして表示する。

check_parity中でerrorを使ってるんですが、
これcheck_parityの型を完全に無視してる気がするんだけど大丈夫なの?(´・ω・`)

それから、decode_parityの定義がたぶんなんかおかしいです。
ほんとはmap(chr.bin2int.check_parity).chop9と書きたかったのですが、
bin2int.check_parityのとこで怒られてしまいました。
(.)演算子がちゃんとわかってない。

-- パリティを先頭に付加
make9_parity :: [Bit] -> [Bit]
make9_parity bits = length (filter odd bits) `mod` 2 : make8 bits

encode_parity :: String -> [Bit]
encode_parity = concat.map(make9_parity.int2bin.ord)

-- まじめにやるならchop nを作ってchop8, chop9はそれを使う方がよい気がする
chop9 :: [Bit] -> [[Bit]]
chop9 [] = []
chop9 bits = take 9 bits : chop9 (drop 9 bits)

check_parity :: [Bit] -> [Bit]
check_parity (x:xs) | x == checked_parity = xs
                    | otherwise           = error "parity error"
                        where checked_parity = length (filter odd xs) `mod` 2

decode_parity :: [Bit] -> String
decode_parity = map(chr.bin2int).map(check_parity).chop9

9.

通信エラーの生じる通信路を用いて、
練習問題8で定義した文字列を通信するプログラムを試せ。
この通信路は最初のビットを落とすとする。
これは、関数tailをビットのリストに適用することで実現できる。

transmit_normal :: String -> String
transmit_normal = decode_parity.channel.encode_parity

transmit_broken :: String -> String
transmit_broken = decode_parity.channel_broken.encode_parity

channel_broken :: [Bit] -> [Bit]
channel_broken = tail

Wolfram AlphaでFizzBuzz

数ヶ月前にこんなツイートをしました。


今更ながら、ちょっと解説記事を書こうと思い立ったので書きます。
ブログ開設による推進力というやつです。

Map[ReplaceAll[GCD[#,15], {15 -> "FizzBuzz", 3 -> "Fizz", 5 -> "Buzz", _ -> #}]&, Range[100]]

上記のコードを実行すると以下のようになります。
f:id:aomori-ringo2:20120626191256p:plain


解説(なげやり編)

Range
Map
ReplaceAll
GCD

リファレンス貼っときました。以上。







嘘ですごめんなさい。わかんねぇよ、という方は丁寧編にお進みください。

解説(丁寧編)

では少しずつ分解していきましょう。
まずはMap, Rangeから。



Map: 第2引数に与えるリストの要素を1つずつ取り出して、第1引数の式を適用させる
Range: 1からnまでの整数のリストを返す

Map[#^2&, Range[10]]


Result: {1, 4, 9, 16, 25, 36, 49, 64, 81, 100}

このとき、#は取り出した要素を表します。
&は、#がある式の最後に付ける必要のある記号で、演算子とかではないです。


続いてGCDです。
GCD: 最大公約数を返す
これをMap, Rangeと組み合わせるとこうなります。

Map[ GCD[#, 15]&, Range[30] ]


Result: {1, 1, 3, 1, 5, 3, 1, 1, 3, 5, 1, 3, 1, 1, 15, 1, 1, 3, 1, 5, 3, 1, 1, 3, 5, 1, 3, 1, 1, 15}


これに対してあとはReplaceAllを適用して完成です。
ReplaceAll: リストに対して変換の規則を適用する

ReplaceAll[ {x, y, z, a}, {x -> m, y -> n} ]


Result: {m, n, z, a}

パターンマッチですね。


このReplaceAllを使って、先ほどのGCDの式に
・15だったら"FizzBuzz"に変換
・3だったら"Fizz"に変換
・5だったら"Buzz"に変換
・それ以外だったらそのままの数字
というのを書きます。

Map[ReplaceAll[GCD[#,15], {15 -> "FizzBuzz", 3 -> "Fizz", 5 -> "Buzz", _ -> #}]&, Range[100]]

できあがり。

駄文

ReplaceAllには「/.」、Mapには「/@」というシンタックスシュガーが用意されています。
(Wolfram Alphaに今回のFizzBuzzの式を入力すると、Inputとして「/.」「/@」という記号に変換されているのが確認できます。)
Mathematicaではもちろん常に使用できますが、Wolfram Alphaでは入力の複雑さによって読み取ってくれたりくれなかったりします。


Mathematica勉強会でも以前この話を出してみたら、

  • 式が読み取れる場合と読み取れない場合の違いがよくわからない
  • シンタックスシュガーを使っても使わなくても本質的には違いがないのに使えなくなる意味がわからない

などの話が現れ、最終的に
「Wolfram Alphaのパーサはクソ」
という結論になっていました。