OCaml の Test::Simple を目コピしてみた その 3
続き。
ʕ•͡ω•ʔ TAPDocument.ml 完全目コピでけたー!
init_document
はなんか初期化しそう。
let init_document doc = let count = count_tests doc in let failures = count_test_failures doc in match doc with | Document(PlanNode(plan)::nodes) -> Document( PlanNode(plan)::nodes @ (if count = plan then [] else [ (create_count_footer count plan) ] ) @ (if failures = 0 then [] else [ (create_failure_footer count failures) ]) ) | Document(nodes) -> Document( nodes @ [ PlanNode(count) ] @ (if failures = 0 then [] else [ (create_failure_footer count failures) ]) )
後は文字列化する関数。
そんでさ、function Document(nodes) ->
は Warning 出るけど、どーすれば回避できるかわかんないって書いたけど、わかった。
let hoge Document nodes = ...
でいいみたい。
引数が複数あってわけわかんない場合は let piyo (Document nodes) =
みたいに括弧つけとこう。
let string_of_status status = match status with | OK -> "ok" | NotOk -> "not ok"
let string_of_diagnostic (Diag lines) = List.map (fun line -> "# " + line + "\n") lines |> List.fold (+) ""
let string_of_directive (Todo s) = "# TODO " + s
let string_of_node node = let emit_diagnostic diag = (match diag with | None -> "\n" | Some diag -> "\n" + (string_of_diagnostic diag)) in match node with | TestCaseNode(status, num, desc, diag) -> (string_of_status status) + " " + num.ToString() + " - " + desc + (emit_diagnostic diag) | TodoTestCaseNode(status, num, desc, dir, diag) -> (string_of_status status) + " " + num.ToString() + " - " + desc + (string_of_directive dir) + (emit_diagnostic diag) | DiagnosticNode(diag) -> (string_of_diagnostic diag) | PlanNode(count) -> "1.." + count.ToString() + "\n"
let string_of_document (Document nodes) = List.map string_of_node nodes |> String.concat ""
ここも。
全部のソースはここに置いてある。
OCaml の Test::Simple を目コピしてみた その 2
続き。
OCaml では文字列の結合は ^
だけど*1、F# では +
だ。
また、OCaml での string_of_int
は F# には存在しないけど、書くとしたらこんな感じになる:
let string_of_int (n:int):string = n.ToString()
DiagnosticNode
を返す関数 2 連発:
let create_failure_footer test_count failure_count = DiagnosticNode( Diag(["Looks like you failed " + failure_count.ToString() + " tests of " + test_count.ToString() + " run."]) )
let create_count_footer test_count plan_count = DiagnosticNode( Diag(["Looks like you planed " + plan_count.ToString() + " tests but " + (if test_count < plan_count then ("only ran " + test_count.ToString()) else ("ran " + ((test_count - plan_count).ToString()) + " extra")) + " ."]) )
*1:F# でも Warning は出るものの、使うことができる
OCaml の Test::Simple を目コピしてみた
これ。
モジュール名は Test.Builder
にしておく。
module Test.Builder type status = | NotOk | Ok type directive = | Todo of string type diagnostic = | Diag of string list type node = | TestCaseNode of status * int * string * diagnostic option | TodoTestCaseNode of status * int * string * directive * diagnostic option | DiagnosticNode of diagnostic | PlanNode of int type test_document = | Document of node list
テストケースをノードとして管理?する。
test_document
が一番トップの型みたいだ。
let rec count_test_nodes nodes count = match nodes with | [] -> count | TestCaseNode(_, _, _, _)::xs | TodoTestCaseNode(_, _, _, _, _)::xs -> count_test_nodes xs count + 1 | DiagnosticNode(_)::xs | PlanNode(_)::xs -> count_test_nodes xs count
関数 count_test_nodes
はノードの数を数える。
具体的には TestCaseNode
と TodoTestCaseNode
を数える。
let count_tests = function Document(nodes) -> count_test_nodes nodes 0
count_tests
は引数が test_document
だった時用の count_test_nodes
だ。
function Document(nodes) ->
は Warning を発生させる。
この Warning をどうやって回避すれば良いのかは不明である。
let count_test_failures = function Document(nodes) -> let rec loop nodes count = match nodes with | [] -> count | TestCaseNode(Ok, _, _, _)::xs | TodoTestCaseNode(Ok, _, _, _, _)::xs | DiagnosticNode(_)::xs | PlanNode(_)::xs -> loop xs count | TestCaseNode(NotOk, _, _, _)::xs | TodoTestCaseNode(NotOk, _, _, _, _)::xs -> loop xs count + 1 in loop nodes 0
関数 count_test_failures
は指定されたノードリストの中から失敗したノードの数を数える。
書いたのは ここ に置いとく。
F# で Either もできたよー
ʕ•͡ω•ʔ 最近 fsharp-mode より tuareg-mode のがいいんじゃないかって思うようになってきた
module Data.Either type either<'a, 'b> = | Left of 'a | Right of 'b let either f g e = match e with | Left a -> f a | Right b -> g b let isLeft x = match x with | Left _ -> true | Right _ -> false let isRight x = match x with | Left _ -> false | Right _ -> true let lefts es = Seq.filter isLeft es let rights es = Seq.filter isRight es
F# で Maybe できたよーーー
ʕ•͡ω•ʔ Haskell の何かを F# で書くと楽しいんじゃ(天才博士風)
というわけで、実装して覚えるシリーズ。
型の定義がこちら:
type Maybe<'a> = | Nothing | Just of 'a
F# 風にするなら、Maybe
を maybe
にするべきかも。
ゆかいな Maybe 関連の関数:
let maybe a f m = match m with | Just x -> f x | Nothing -> a let isJust m = match m with | Nothing -> false | Just _ -> true let isNothing m = match m with | Nothing -> true | Just _ -> false let fromJust x = match x with | Nothing -> failwith "Maybe.fromJust: Nothing" | Just x -> x
だけど、F# には option
というのが既にあるので、そっちを使いましょう。
FSharp やってみた2
その 2。
add
という関数を定義して、引数を渡して評価してみる:
> let add x y = - x + y;; val add : x:int -> y:int -> int > add 2 2;; val it : int = 4
関数のボディ部分を折ってるけど、インデントしないと Warning が出るので注意。
あと、終了する時は:
> exit 0;;
か:
> #q;;
でできる。
型アノテーション
フレーズを渡したら、leet 語っぽく返してくれる関数を定義してみよう:
> let toHackerTalk phrase = - phrase.Replace( 't', '7' ).Replace( 'o', '0' );; phrase.Replace( 't', '7' ).Replace( 'o', '0' );; --^^^^^^^^^^^^^^ /home/rihine/workspace/hello-fsharp/stdin(4,3): error FS0072: Lookup on object of indeterminate type based on information prior to this program point. A type annotation may be needed prior to this program point to constrain the type of the object. This may allow the lookup to be resolved.
エラーになった。
どうやら、引数 phrase
が object
っぽく解釈されてるものの、object
には当然 Replace()
なんてメソッドはないわけで、
そんなもん無いよって言ってるらしい。
アノテーションを付けてみるといいらしい:
> let toHackerTalk (phrase:string) = - phrase.Replace( 't', '7' ).Replace( 'o', '0' );; val toHackerTalk : phrase:string -> string
関数は実際、値
ソースコードをファイルに書いて実行することにする。
引数を 4 倍する関数を定義する:
// quadruple.fsx let quadruple x = let double x = x * 2 double ( double x );; printfn "%d" (quadruple 4);;
関数の中で関数を定義できる。という感じ。
普通にファイル名を指定するだけで実行してくれる:
% fsharpi ./quadruple.fsx
16
関数を引数に取る高階関数を使うコード:
// chrisTest.fsx let chrisTest test = test "Chris" ;; let isMe x = if x = "Chris" then "it is Chris!" else "it's someone else" ;; printfn "%A" (chrisTest isMe);;
ラムダ関数
前の add
関数はラムダ式で書くとこう書ける:
> let add = (fun x y -> x + y);; val add : x:int -> y:int -> int
その2:
> let twoTest test = - test 2;; val twoTest : test:(int -> 'a) -> 'a > twoTest (fun x -> x < 0);; val it : bool = false
参照
FSharp やってみた
良い感じのブラウザだと Try F# で式を評価できるらしいんだけど、できないブラウザだったので、処理系をインストールして式を評価してみるよ。
インストール
yaourt で fsharp をインストールできるよ。
% yaourt -S fsharp
REPL 起動
REPL を起動する時は fsharpi って打ってね:
% fsharpi
れっと
let
を使って変数?を定義?することができるよ:
> let lucky = 3 + 4;; val lucky : int = 7
あと、式の最後には ;;
(セミコロン 2 つ)を付けてね。
前に定義してた変数?も参照できるよ:
> let unucky = lucky + 6;; val unucky : int = 13
とりあえず変数?を定義?して
別の値を設定することもできるよ。
シャドウイングってやつ?
> let duplicated = "original value";; val duplicated : string = "original value" > let duplicated = "new value";; val duplicated : string = "new value"
れっと☆みゅーたぶる
let
した変数?は二度と変更することができないよ。
どうしても変更したい変数?を使う時は let mutable
で定義?するといいみたい:
> let mutable modifiable = "original value";; val mutable modifiable : string = "original value" > modifiable <- "new value";; val it : unit = () > modifiable;; val it : string = "new value"
「代入」する場合は <-
(左向き矢印)を使うみたい。
型
型推論されて int
とか float
とか string
になってるー!!! というやつ。
> let anInt = 10;; val anInt : int = 10 > let aFloat = 20.0;; val aFloat : float = 20.0 > let aString = "I'm a string!";; val aString : string = "I'm a string!"
出力
へろーわーるどするときは printfn
を使うよ:
> printfn "Hello, World from F# REPL!!";; Hello, World from F# REPL!! val it : unit = ()
printf
みたいに書式をあれしてこれできる:
> printfn "The answer is %d" 42;; The answer is 42 val it : unit = ()
Lisp でいうと:
(printfn "The answer is %d" 42) ;; | The answer is 42 ;; => nil
(๑´ڡ`๑) おしまい
参照
フォントを Roboto にした
タイトル下
前やったときは記事上とかにしてたんだけど、これだと複数になっちゃうかも?って思ってタイトルの下に書いた。
<!-- タイトル下 //--> <link href='https://fonts.googleapis.com/css?family=Roboto' rel='stylesheet' type='text/css'> <link href='https://fonts.googleapis.com/css?family=Roboto+Mono' rel='stylesheet' type='text/css'> <!--// タイトル下 -->
デザイン CSS
常日頃からソースコードとかのフォントおかしいなーと思っていたので、Roboto Mono にすることにした。
/* <system section="theme" selected="wideboard"> */ @import "/css/theme/wideboard/wideboard.css"; /* </system> */ /* <system section="background" selected="fff"> */ body { background: #fff; font-family: 'Roboto', sans-serif; } /* </system> */ /* <customize!!> */ .entry-content pre { font-family: 'Roboto Mono', sans-serif; } /* </customize!!> */
(๑´ڡ`๑) いい感じ
後日談
結論から言うと、VSCode では C# なプロジェクトを作れないのだ。ということがわかった。
ASP.NET なのしかサポートしてないので、やりたいなら Visual Studio コミュニティーエディションをインストールしろということらしい*1
実のところ、このような:
- task: name: build is-shell-command: true show-output: true args: [ -c hello.c -o hoge ] command: gcc
タスクを記述する外部 SDL では*2依存関係を記述できない。
このことは JS 製のタスクランナーやビルドツールが古典的なビルドツールである make を超えることができない理由の一つである*3。
% dry add ./src/hello.c
とかやると、.velbet/tasks.yml とかに hello.c が追加されるような仕組みである。
ビルドする時は:
% dry build
とかやるとビルドできるのだ。
VSCode を yaourt でいんすとろーるしてみた。
まだ良くわかっていない*1。
ビルドタスクは以下のような .vscode/tasks.json に書くらしい。
{ // See http://go.microsoft.com/fwlink/?LinkId=733558 // for the documentation about the tasks.json format "version": "0.1.0", "command": "dotnet", "isShellCommand": true, "args": [], "tasks": [ { "taskName": "build", "args": [ ], "isBuildCommand": true, "showOutput": "silent", "problemMatcher": "$msCompile" } ] }
複数のファイルをビルドするときはどうするのだろう?とかとりあえずは 1 つのファイルをビルドしたいのだけれど、 どーすれば良いのだろうなどと考えていた。
これを見た時、「ふーむ、こんな書き方で依存関係を記述できるものなのだろうか…」と思った。
さて、ここで大昔に考えたビルドランナーの velvet(仮)に登場して貰う。
タスクを記述した例えば .velvet/tasks.yml みたいなファイルに下記のように書いてあったとする:
- task: name: build is-shell-command: true show-output: true args: [ -c hello.c -o hoge ] command: gcc
.vscode/tasks.json を YAML に直しただけ(あと必要なさそうなのを削った)だけれど、
% velvet build
あるいは:
% velvet
で実行できるといいなーと思う。
こういう書き方だと、1 タスクにつき、1 コマンドでなんとも生産性が悪そうだ。
(。・_・。) うーん………。もうちょっと調べてみるか……