2009-06-26
【第5.5話】Lightweight Syntax って何?
少年オッカムル | | ![]()
メールの着信音で僕は顔をあげる。あ、リンダさんからだ。なんだろ?
「何? メール?」
向かいに座ったミラっちが僕を見ていた。そう。今日は腰を落ち着けて予習しようと思い、またミラっちと喫茶店「ガロアの時間」にお邪魔してるところだ。
「うん。リンダさんから」
そう答えた僕にミラっちは「ふぅん」と興味なさそうな返事をする。
「えーと、、なになに・・・」
ごめーん! この前は私も良く分かってなかったから、F# の "Lightweight Syntax" について何も説明してなかったんだけど、これは知ってた方が良さそうだよ。 まず、F# だとTABキーが使えなくなっちゃってた件なんだけど、この "Lightweight Syntax" の機能を "OFF" にすると使えるようになるみたい。具体的にはプログラムの先頭に「#light "off" 」って入れるとイイんだって。私が教えてもらった人の話だと、OCamlプログラマ でない人達の学習コストを下げる目的で作られた構文が"Lightweight Syntax"って言うみたい。でも良く考えてみるとキミ達は OCaml の本を読みながら勉強してるワケだから、今のステップではこの機能を OFF にした方がむしろ分かりやすいんじゃないかな? ちょっと試してみてね! ただ、コレをやっちゃうと今まで使えてた"printfn"みたいな関数がそのままだと使えなくなっちゃうんだよね。そこら辺をドウするか?についてはこのサイトに書かれてるから、チェックしてみて;-) http://blogs.msdn.com/dsyme/archive/2006/08/24/715626.aspx あ、そうそう。URLを見れば分かると思うケド、記事の日付がちょっと古いから「 "Lightweight Syntax" はオプション」みたいな書かれ方になってるトコが実際にはデフォルトになってる点、気をつけてね。 それじゃ、予習頑張って♪ りんだ
最後の名前がひらがなになってるのがちょっとカワイイな。なんて僕がニヤけそうになってたところにミラっちから声がかかる。
「何?勉強会が延期になるとかそういう話?」
「あ、いや。この間の補足みたいな話だよ。ちょっと待って。今、リンダさんに教えてもらったページにアクセスしてみるから」
なになに・・・ってリンダさん、このサイト英語じゃん!
「えーっと・・・」
The F# indentation-aware syntax option is a conservative extension of the explicit language syntax, in the sense that it simply lets you leave out certain tokens such as in and ;; by having the parser take indentation into account. This can make a surprising difference to the readability of code.
「F# のインデントを意識した構文オプションは、明示的な構文の保守的な拡張です。。んー、なんだドウいう意味だ?」
「アタシも本職じゃないから上手く訳すのは難しいけど、要するに言いたいコトは『in とか ;; みたいな特定のトークンを使わなくても、コードを解析してくれるパーサがインデントを見て勝手にプログラムの構造を判断してくれるよ』ってことみたいね」
とミラっち。そうだよね。中身が掴めれば別に日本語の文章に直す必要もないかー。
「うん。で、後は『驚くほどコードの可読性が上がるよー』って事を主張してるみたいだね」
続けて読んでいこう。
[ Note: This feature is similar in spirit to the use of indentation by Python and Haskell, and we thank Simon Marlow (of Haskell fame) for his help in designing this feature and sketching the implementation technique. We also thank all the F# users at MSR Cambridge who've been helping us iron out the details of this feature. ]
「なんか良く分からないケド、Python とか Haskell って言語が OCaml 以外にあって、そこで使われてるテクニックに似てるもんだよ、みたいな話なのかしら?」
「あー、そういや 『Python はインデントを使ってどうしたこうした』って話、どっかのブログで見た気がするな」
Compiling your code with the indentation-aware syntax option is useful even if you continue to use explicit tokens, as it reports many indentation problems with your code and ensures a regular, clear formatting style. The F# library is written in this way.
「ここら辺は、要するに『これは便利でイイものだから是非使ってね!』とかそんな感じよね」
「うん。『トークンを使いたくないとか思ってないよ、って人にとってもコードの可読性が高まるとイイ事あるから使ってね。F# のライブラリもこういう書き方になってるよ』ってあたりかな」
In this article we call the indentation-aware syntax option the "light" syntax option. It is also occasionally called the "hardwhite" or "white" option (because whitespace is "hard", i.e. significant as far as the lexer and the parser is concerned).
「で、ここは呼び名の説明をしてるくらいね」
「だね。」
The light syntax option is enabled using the #light directive in a source file. This directive scopes over all of the subsequent text of a file.
「あー、リンダさんの言ってたのは多分、ここら辺のコトだな」
「え?」
「ああ、『資料の日付が古いから"light syntax" ってのがオプションのように書かれてるけど、実際にはこの構文がデフォルトになってるよ』って話が、さっきのメールに書いてあったんだ」
「ふーん。そうなんだ」
なんだかつまらなさそうな顔をするミラっち。あれ?疎外感とかそういうの??
「えっと、、とりあえず進もうか」
When the light syntax option is enabled, comments are considered pure whitespace. This means the indentation position of comments is irrelevant and ignored. Comments act entirely as if they were replaced by whitespace characters.
「"light syntax" が有効になってる場合、コメントが空白文字と置き換えられて無視されるってコトみたいね」
「うん。だね」
よし、どんどん進むぞ。
TAB characters may not be used when the light syntax option is enabled. You should ensure your editor is configured to replace TAB characters with spaces, e.g. in Visual Studio 2005 go to "Tools\Options\Text Editor\F#\Tabs" and select "Insert spaces".
「"light syntax" が有効になってる場合、TAB文字は使うな。って話ね。ここは前回で体験済み」
「そうだね。あ、僕らが使ってるのは Visual Studio じゃないから TAB をスペースに置き換えるようなエディタのセッティング方法ってのは、後で調べてみてもイイのかも」
Using the light syntax option makes code clearer by doing three things:
「"light syntax" の利点を3つ、ってところかしら」
「うん。ここからが本題っぽいね」
Fewer tokens. Nearly all end-of-line separator tokens become optional in well-indented code. In particular, ;;, in and ; tokens can generally be omitted.
「トークンが減らせる。良くインデントされたコードにおいては、ほぼ全ての行末のセパレータ(区切り文字?)がオプションになる。特に ;; や ; が省略できるようになる。」
「うん。そして次に書いてあるのが、、」
Clearer disambiguation. It uses indentation to disambiguate the parsing of certain constructs, e.g. nested if/then/else blocks and nested match blocks. This greatly reduces the number of parentheses in code with nested branching constructs.
「曖昧さの回避。インデントを使うことで、入れ子になってるコードのブロックの構造解析が楽になる。入れ子になった分岐構造なんかで使う括弧の量が減らせる、、とかそんな感じかな。そういや前回のサンプルコードは見やすかったっけ」
「そうね。私も修正した後のコードはちょっと見づらい気がしてた」
Sanity checks. It applies additional sanity checks on formatting, reporting places where "undentation" has been used. Unindentation is where a language construct has been used at a column position that is "undented" from an enclosing construct, which breaks the important principle that nested constructs appear at increasing column positions. Some manifestations of undentation are permitted in certain positions in the language syntax.
「これは、、、何?」
唸るミラっち。
「うーん。健全性のチェックとか言われても、確かにピンと来ないねぇ。。インデントされてない箇所があったら警告をあげてくれるとか、そういう話かな、、良く分からないけど。。」
「ま、今のところはそんなもんでイイか」
ミラっちはこういうところ、本当に切り替えが早い。見習わないと。
「そだね。で、後に続くのは、、、ああ。具体例っぽいね」
「そうね」
// When the light syntax option is // enabled top level expressions do not // need to be delimited by ';;' since every construct // starting at first column is implicitly a new // declaration. NOTE: you still need to enter ';;' to // terminate interactive entries to fsi.exe, though // this is added automatically when using F# // Interactive from Visual Studio. #light printf "Hello" printf "World"
「"light syntax" が有効の場合は各行の最後にいちいち ;; をつける必要が無いけど、これが"OFF"になってる場合は、、」
// Without the light syntax option the // source code must contain ';;' to separate top-level // expressions. // // // printf "Hello";; printf "World";;
「こんな感じで ;; が必要になる、ってコトか。ああ、これがリンダさんの言ってた『コレをやっちゃうと今まで使えてた"printfn"みたいな関数がそのままだと使えなくなっちゃう』って話っぽいな」
「みたいね。アタシは読んでないから知らないけど」
うう。。。ミラっち、また機嫌悪くなってきた。。
「えっと、、次いこうか」
// When the light syntax option is
// enabled 'in' is optional. The token after the '='
// of a 'let' definition begins a new block, where
// the pre-parser inserts an implicit separating 'in'
// token between each 'let' binding that begins at
// the same column as that token.
#light
let SimpleSample() =
let x = 10 + 12 - 3
let y = x * 2 + 1
let r1,r2 = x/3, x%3
(x,y,r1,r2)
「"light syntax" が有効の場合はin が要らない、って書いてあるみたいだけど、そもそも僕ら、まだ教科書に in って出てきてないよね。。」
「確かに。まぁ、頭の隅に入れておくことにしよっか?」
「だね。そういう意味では、次の done ってのも見たことないし、スコープってのもまだ良く分からないや。」
「てか、これ以降の話は全部そんな感じじゃん?」
と、ミラっちの的確なツッコミ。
「あー、確かに」
「まぁ、何にしても『インデントには注意!』ってコトね」
「そだね。。よし! んじゃ、1つ試しにやってみようか? 前回上手く動かなかったファイルを使ってさ」
「そうね。イイんじゃない?」
早速前回のファイルをエディタで読み込む。
(* 目的:月と日を受け取ってきたら星座を返す *) (* seiza : int -> int -> string *) let seiza tsuki hi = if tsuki = 1 then if 1 <= hi && hi <= 19 then "山羊座" else if 20 <= hi && hi <= 31 then "水瓶座" else "なし" else if tsuki = 2 then if 1 <= hi && hi <= 18 then "水瓶座" else if 19 <= hi && hi <= 29 then "魚座" else "なし" else if tsuki = 3 then if 1 <= hi && hi <= 20 then "魚座" else if 21 <= hi && hi <= 31 then "牡羊座" else "なし" else if tsuki = 4 then if 1 <= hi && hi <= 19 then "牡羊座" else if 20 <= hi && hi <= 30 then "牡牛座" else "なし" else if tsuki = 5 then if 1 <= hi && hi <= 20 then "牡牛座" else if 21 <= hi && hi <= 31 then "双子座" else "なし" else if tsuki = 6 then if 1 <= hi && hi <= 21 then "双子座" else if 22 <= hi && hi <= 30 then "蟹座" else "なし" else if tsuki = 7 then if 1 <= hi && hi <= 22 then "蟹座" else if 23 <= hi && hi <= 31 then "獅子座" else "なし" else if tsuki = 8 then if 1 <= hi && hi <= 22 then "獅子座" else if 23 <= hi && hi <= 31 then "乙女座" else "なし" else if tsuki = 9 then if 1 <= hi && hi <= 22 then "乙女座" else if 23 <= hi && hi <= 30 then "天秤座" else "なし" else if tsuki = 10 then if 1 <= hi && hi <= 23 then "天秤座" else if 24 <= hi && hi <= 31 then "蠍座" else "なし" else if tsuki = 11 then if 1 <= hi && hi <= 21 then "蠍座" else if 22 <= hi && hi <= 30 then "射手座" else "なし" else if tsuki = 12 then if 1 <= hi && hi <= 21 then "射手座" else if 22 <= hi && hi <= 31 then "山羊座" else "なし" else "なし" (* テスト *) let test1 = seiza 6 11 = "双子座" let test2 = seiza 6 30 = "蟹座" let test3 = seiza 9 17 = "乙女座" let test4 = seiza 10 7 = "天秤座"
「えーっと、、今日学んだ理屈で考えて直してみると、、だ」
#light "off" (* 目的:月と日を受け取ってきたら星座を返す *) (* seiza : int -> int -> string *) let seiza tsuki hi = if tsuki = 1 then if 1 <= hi && hi <= 19 then "山羊座" else if 20 <= hi && hi <= 31 then "水瓶座" else "なし" else if tsuki = 2 then if 1 <= hi && hi <= 18 then "水瓶座" else if 19 <= hi && hi <= 29 then "魚座" else "なし" else if tsuki = 3 then if 1 <= hi && hi <= 20 then "魚座" else if 21 <= hi && hi <= 31 then "牡羊座" else "なし" else if tsuki = 4 then if 1 <= hi && hi <= 19 then "牡羊座" else if 20 <= hi && hi <= 30 then "牡牛座" else "なし" else if tsuki = 5 then if 1 <= hi && hi <= 20 then "牡牛座" else if 21 <= hi && hi <= 31 then "双子座" else "なし" else if tsuki = 6 then if 1 <= hi && hi <= 21 then "双子座" else if 22 <= hi && hi <= 30 then "蟹座" else "なし" else if tsuki = 7 then if 1 <= hi && hi <= 22 then "蟹座" else if 23 <= hi && hi <= 31 then "獅子座" else "なし" else if tsuki = 8 then if 1 <= hi && hi <= 22 then "獅子座" else if 23 <= hi && hi <= 31 then "乙女座" else "なし" else if tsuki = 9 then if 1 <= hi && hi <= 22 then "乙女座" else if 23 <= hi && hi <= 30 then "天秤座" else "なし" else if tsuki = 10 then if 1 <= hi && hi <= 23 then "天秤座" else if 24 <= hi && hi <= 31 then "蠍座" else "なし" else if tsuki = 11 then if 1 <= hi && hi <= 21 then "蠍座" else if 22 <= hi && hi <= 30 then "射手座" else "なし" else if tsuki = 12 then if 1 <= hi && hi <= 21 then "射手座" else if 22 <= hi && hi <= 31 then "山羊座" else "なし" else "なし" (* テスト *) let test1 = seiza 6 11 = "双子座" let test2 = seiza 6 30 = "蟹座" let test3 = seiza 9 17 = "乙女座" let test4 = seiza 10 7 = "天秤座" printfn "test1: %b" test1;; printfn "test2: %b" test2;; printfn "test3: %b" test3;; printfn "test4: %b" test4;;
「これで動くようになるのかな?」
> [Loading C:\Program Files\SharpDevelop\3.0\bin\test10.fs] test10.fs(48,26): error FS0003: This value is not a function and cannot be applied. Did you forget to terminate a declaration?
「・・・ありゃ?」
と、ここでまたメールの着信音が。リンダさんだ。おいおい、ひょっとして僕等をどこかでみててくれたりするのかな? なんてワケないか。
ごめーん。そう言えば、イケ太さんって人が”「printfn "test1: %b" test1」以下の行については、トップレベルに直に式を書くのでなくて、なにかに束縛する必要がある。例えば 『let () = ... 』 とか”って言ってたの忘れてた。へへー、ゴメン! つまり、こんな感じにする必要があるみたい。 let () = printfn "test1: %b" test1; printfn "test2: %b" test2; printfn "test3: %b" test3; printfn "test4: %b" test4 「束縛」とかって話はまた別途説明する予定だから、今回は一応「そんなもんなのね」くらいで覚えておいてくれればイイよー。 ほんじゃね! りんだ
「へぇ。 『let () = ... 』 に束縛かぁ、、、ちょっとまだピンとこないけど試してみよう」
#light "off" (* 目的:月と日を受け取ってきたら星座を返す *) (* seiza : int -> int -> string *) let seiza tsuki hi = if tsuki = 1 then if 1 <= hi && hi <= 19 then "山羊座" else if 20 <= hi && hi <= 31 then "水瓶座" else "なし" else if tsuki = 2 then if 1 <= hi && hi <= 18 then "水瓶座" else if 19 <= hi && hi <= 29 then "魚座" else "なし" else if tsuki = 3 then if 1 <= hi && hi <= 20 then "魚座" else if 21 <= hi && hi <= 31 then "牡羊座" else "なし" else if tsuki = 4 then if 1 <= hi && hi <= 19 then "牡羊座" else if 20 <= hi && hi <= 30 then "牡牛座" else "なし" else if tsuki = 5 then if 1 <= hi && hi <= 20 then "牡牛座" else if 21 <= hi && hi <= 31 then "双子座" else "なし" else if tsuki = 6 then if 1 <= hi && hi <= 21 then "双子座" else if 22 <= hi && hi <= 30 then "蟹座" else "なし" else if tsuki = 7 then if 1 <= hi && hi <= 22 then "蟹座" else if 23 <= hi && hi <= 31 then "獅子座" else "なし" else if tsuki = 8 then if 1 <= hi && hi <= 22 then "獅子座" else if 23 <= hi && hi <= 31 then "乙女座" else "なし" else if tsuki = 9 then if 1 <= hi && hi <= 22 then "乙女座" else if 23 <= hi && hi <= 30 then "天秤座" else "なし" else if tsuki = 10 then if 1 <= hi && hi <= 23 then "天秤座" else if 24 <= hi && hi <= 31 then "蠍座" else "なし" else if tsuki = 11 then if 1 <= hi && hi <= 21 then "蠍座" else if 22 <= hi && hi <= 30 then "射手座" else "なし" else if tsuki = 12 then if 1 <= hi && hi <= 21 then "射手座" else if 22 <= hi && hi <= 31 then "山羊座" else "なし" else "なし" (* テスト *) let test1 = seiza 6 11 = "双子座" let test2 = seiza 6 30 = "蟹座" let test3 = seiza 9 17 = "乙女座" let test4 = seiza 10 7 = "天秤座" let () = printfn "test1: %b" test1; printfn "test2: %b" test2; printfn "test3: %b" test3; printfn "test4: %b" test4
「どうだっ?!」
祈るような気持ちでファイルを読み込む。
> [Loading C:\Program Files\SharpDevelop\3.0\bin\test10.fs] test1: true test2: true test3: true test4: true namespace FSI_0005 val seiza : int -> int -> string val test1 : bool val test2 : bool val test3 : bool val test4 : bool
「やったー!動いたーっ!!」
嬉しくて思わずバンザイとかしちゃう僕。
ミラっちは何だかちょっと釈然としてない様子。。まぁ、僕もスッキリしたか?って言うと微妙なところはあるんだけど、、そこはリンダさんの言葉を信じよう。うん。
「まぁ、今の時点での目的は、あくまで教科書に出てくる内容との差分を減らそうってコトだからね。詳しいメカニズムは追々やっていくってコトで今は教科書に沿ってやっていくことにしようか?」
「…うーん。そうね。今は要チェックポイントとして覚えておくって事にして先に進むのが良いカモ」
と、ミラっち。
「よし。それじゃあ、もう一頑張りしておきますか!」
あ、、その前に…と。
「マスター! コーヒーのお替わりお願いしまーす!」
「アタシも爽健美茶もう1杯!」
夕飯の前だし、ミルクは入れないでおこうかな、、、って言うかさ、、、「イケ太さん」ってリンダさんの何なの?
次回につづく。
camlspotter
2009/06/26 10:21
なして爽健美茶?と爽健美茶を飲みながら思いました。オフィスでも結構人気です。
hamatsu1974
2009/06/26 14:34
ミラっちは健康(てかスタイル?)を気にしてジュースとかは飲まないという設定なのです。お茶の中でも何故に爽健美茶なのか?と言えば、まぁ何となくとしか、、(笑)
