2011年12月26日

F#初心者による in キーワドの考察

   このエントリーをはてなブックマークに追加 Clip to Evernote
このエントリは、F# Advent Calendar 2011のボーナスステージへの参加記事です。



実は、この記事は、僕のF#専用ブログGushwell's F# Programming Diaryに掲載したものです。
しかし、このブログがアクセス不可状態になってしまったため、今までアップした記事が
すべて閲覧できなくなってしまいました。なんとか、CD-Rへバックアップしたものから、
いくつかの記事の下書きを見つけることができましたので、今回アップするこの記事も、
その下書きをもとに、再度清書し直したものです。

この記事は、僕がF#を学び始めて疑問に思った in についての考察であり、
これからF#学ぼうと思っている方には、それなりに有用な内容になっているのではと勝手に思ってます。

では、本題に入りましょう。


以下のコードを見て下さい。


僕がF#の勉強を始めたころには、この in の意味が全く分かりませんでした。
なので、これからF#を学ぶ人のために、ここで、in キーワードについて
考えてみたいと思います。

さて、上のF#のコードですが、これは、

printfn "%A" x の中だけで、x を使いますよ、その Xは 1 に束縛されていますよ。

という意味になります。

では、


はどうでしょうか。これは、abs関数が値を持つので、


と警告が出てしまいます。
F#では、原則として式の値を捨て去ることができないことを思い出しましょう。

一方、最初に示したコードでは、printfn は、unit型なので、値はありません。
だから最初のコードは問題ありません。

abs を使ったコードに話を戻すと、値を捨て去らないように、
absの返す値を何らかの変数に束縛するなどのコードが必要となります。
そこで、


とzに代入(束縛)するようにしないといけません。
例えば、このコードに続けて、


とzを参照するコードを書くわけですね。書くまでもないが、10 が表示されます。
では、次のコードはどうでしょうか。


このコードの場合、y は、1行目だけの局所変数になっているので、3行目でコンパイルエラーになります。

次のようなコードの場合は、問題ありません。ただしくコンパイルができます。


これから分かることは、in の右側が主であり、in の左側が従だということです。
僕は、let で始まるんだから、y = 10 が主処理であるという感覚から抜け切れなかったため、
in が理解できなかったようです。

では、関数定義を使ったときの in について調べてみます。

まずは、一番簡単な例。


かなり、無理やり感は、否めないけど...
この原型は、


であり、2を局所変数 x に束縛させた例です。つまり、


と同じです。
もう少し、まともな例を書いてみます。


これは、局所[変数]ではなく、局所[関数]を使っていますが、考え方は同じです。
骨格は、


なのですが、このadd関数を局所関数として定義しているということになります。

それでは、僕が、in について調べてみようと思った発端となったコードを見てみます。


いきなりこのコードをみて、inが何を意味しているのかを理解するのは、F#初心者には
結構難しいじゃないかと思います。
少なくとも僕は、理解できませんでした。
しかし、ここまで順に説明を読んできた方には、理解できるんじゃないでしょうか?


が、この式の骨格であり、
引数2つのSumTotal関数は、引数1つのSumTotalの[中]でのみ利用できる局所関数として、
定義しているということです。

これが僕のたどり着いた in の意味です。

なお、ここまで書いておいてなんですが、実際のF#では、複数行に分けた場合、in は
省略できますので、上のコードは、以下のコードと同じです。


それでは最後に、前述した dbl 関数の定義について in を使わないコードに
書きなおしてみます。


これならば、何も悩む必要はないですね。

つまり、ワンライナーを使わなければ、in は不要ということです。
僕は、ワンライナー嫌いなので(単に理解できる頭が無いというだけ?)、
in を使うことは無いと思いますが、
他の人の書いたコードを読む場合は、この in の知識が役に立つと思われます。

  

Posted by gushwell at 00:30Comments(0)TrackBack(0)

2011年11月20日

F#:どう書く?org - データの整列(ソート)

   このエントリーをはてなブックマークに追加 Clip to Evernote
どう書くorg?の 「データの整列」をF#で書いてみました。

(x, y) の座標情報を以下の2種類の方法で整列する機能を実現してください。

(x, y) の辞書順(まず x で昇順に整列して、x が同じデータに対して y で昇順に整列する)
(0, 0) からの距離の昇順

データの表現方法はタプルなり構造体/オブジェクトなり各自で適当に選んで下さい。

適当に選んでください、とのことなので、タプルを選択して書いてみました。


sortByLexicalOrder 関数が、辞書順にソートする関数。
sortByDistance関数が、距離の昇順でソートする関数です。
sortByLexicalOrderは、LINQを使ってかいてみました。

C#では引数の型を指定するのは当たり前で、全然苦にならないですが、
F#だと、ものすごく面倒に感じるのはなぜなんでしょうか?
単に、F#に慣れていないせいだとは思いますが。。。

sortByLexicalOrder 関数は、以下のように書く事もできますね。

---------
追記 11/23

sortByLexicalOrdeは、以下のコードでOKでした。


ということは、わざわざ関数にするまでもないってことですね。
  
Posted by gushwell at 19:59Comments(6)TrackBack(0)

2011年10月31日

F#:どう書く?org - 外部実行ファイルの呼び出し

   このエントリーをはてなブックマークに追加 Clip to Evernote
どう書くorg?の 「外部の実行ファイルを呼び出し」を F#で解いてみました。

外部の実行ファイルを呼び出して実行してください。
ただし、実行中にプログラムの実行をブロックする版と、しない版の二つを作ってください。

ここでの「ブロック」とは「子プロセスの終了を待つかどうか」ということのようです。
.NET Frameworkのクラスを使うだけなので、難しいところはありませんね。


Run 関数と RunAndWaitという2つの関数を定義しました。
Run関数は、ブロックしない版、RunAndWaitはブロックする版です。
両方ともProcessクラスを使っていますが、ちょっと使い方を変えてみました。
深い意味はないです。

Main関数では、メモ帳を起動し、メモ帳が終了したら、ワードパッドを起動しています。
ワードパッドは、9秒後に終了していなければ、Killメソッドで、強制終了させています。


  
Posted by gushwell at 23:00Comments(0)TrackBack(0)

2011年09月20日

F#:どう書く?org 「文字列の均等分割」

   このエントリーをはてなブックマークに追加 Clip to Evernote
どう書く?orgの「文字列の均等分割」をF#で書いてみました。
一行の文字列を指定した数の行にできるだけ文字数が均等になるように分割してください.
ただし,除算や剰余算を使わないで書いてみてください.

sample = "ゆめよりもはかなき世のなかをなげきわびつゝあかしくらすほどに四月十よひにもなりぬれば木のしたくらがりもてゆく"

divid 4 sample =>
"ゆめよりもはかなき世のなかを"
"なげきわびつゝあかしくらすほ"
"どに四月十よひにもなりぬれ"
"ば木のしたくらがりもてゆく"


問題では、「除算や剰余算を使わないで」とありましたが、それは無視して書いています。
なので、1行の幅の文字数は、簡単に求められるので、
あとは、String.Substringメソッドで、文字列を分割して行くだけです。
気をつける点といえば、「できるだけ均等に」ということなので、1行の文字数は、
最初に求めるのではなく、再帰関数のなかでその都度求めている点でしょうか。


問題での例では、divide関数内で、プリントもしていましたが、
ここでは、分割する関数ではプリントはせずに、分割した結果を返すようにしています。
このdivide関数は、string listを返す関数で、再帰呼び出しにより実現しています。
:: 演算子で、 左側の要素と右側のリストを連結しています。


C#のネーミング規則に慣れてしまっているので、ついつい、関数名を大文字で始めてしまいます。
規模の大きなプログラムでは、すべてが小文字で始まると、どれが関数でどれが変数なのかの
判断が付きにくいと思うのですが、他の人はそんなふうには感じないのかな...
まあ、慣れの問題なのでしょうが、そもそも2,3週間に一回程度、F#をいじるだけですから、
慣れようがないです。

  
Posted by gushwell at 22:00Comments(0)TrackBack(0)

2011年09月08日

F#:どう書く?org 「制限時間内のキー入力検査」

   このエントリーをはてなブックマークに追加 Clip to Evernote

どう書く?org 「制限時間内のキー入力検査」 をF#で解いてみました。

ちょっと長いですが、そのまま引用させていただきます。

ユーザーの入力を検査する関数 InputCheckerを作成して 3回連続実行してください。
関数 InputCheckerは、以下の仕様を満たしてください。

引数に 時間(秒) Nと文字列 S を受け取ること。
ユーザからの標準入力とあらかじめ指定された S を比較し、 一致すれば「OK」、一致しなければ「NG」を標準出力へ出力すること。
ユーザーの入力がN秒以内に完了しなかった場合は、比較せず 「TIME OUT」を標準出力へ出力すること。
経過時間はユーザーが入力を開始した地点から計測すること。
ENTER キーの入力によってユーザー入力が完了したと仮定すること。
ユーザの入力が完了するまでは、完了を待ち続けること
たとえば、「InputCheker(5, "ABCDEF")」と指定した場合、 出力例はこんな感じです。

1. input(ABCDEF) =>ABCDEF<ENTER>
    1. result => OK
    2. result => NG
    3. result => TIME OUT

1. input(ABCDEF) =>

と出力して入力待ちをし、ユーザーが「ABCDEF<ENTER>」を入力したとき、 入力開始から5秒以内ならば「OK」、
5秒をこえていれば「TIME OUT」を出力します。 このとき、ユーザーがキーを押下しなければ1. を出力してから
たとえ10秒たっていても「TIME OUT」にはならないので注意してください。
時間計測はあくまでユーザーが入力を開始してからです。

 

入力が開始されたかどうかをどうやって判断するのかがわかれば、それほど難しくはありませんね。
Console.KeyAvailable プロパティで、その判断ができます。このプロパティが、trueになるまで繰り返し、待ちます。

キーが入力されたならば、ReadLineで 1行読み込み、Enterキーが押されるまでの時間を計測します。
制限時間をオーバーしていれば、"TIME OUT"を表示します。
制限時間内ならば、入力値と引数の値を比較し、"OK", "NG" のいずれかを表示します。

inputChecker関数では、制限時間と、比較する文字列を受け取ります。

あまり、F#らしい箇所がないので、C#プログラマーにも雰囲気で何をやっているかがわかると思います。

ENTERキーを押す前に、"TIME OUT"と表示するとなると、また違ったアプローチが必要ですね。
ReadLine じゃなくて、ReadKeyを使うことになるのかな?

  
Posted by gushwell at 22:00Comments(0)TrackBack(0)