Hatena::Grouphackathon

zrail (a.k.a. tobira17, h14i, ...) の Learning Log

2016-08-21

[][] どうでもいいメモ 01:36  どうでもいいメモ - zrail (a.k.a. tobira17, h14i, ...) の Learning Log を含むブックマーク はてなブックマーク -  どうでもいいメモ - zrail (a.k.a. tobira17, h14i, ...) の Learning Log

let f = function
    [] -> 0
  | _ :: _ -> 1
;;
(* とか *)
let f x = match x with
    0 -> true
  | _ -> false
;;

みたいな書き方と

let f = function
  | [] -> 0
  | _ :: _ -> 1
;;
(* とか *)
let f x = match x with
  | 0 -> true
  | _ -> false
;;

みたいな書き方ありますよね。'|' の書き方。

見た目以外の差ってあるんすかね?

一行で書くときは上のほうが自然ですよね。

let f = function [] -> 0 | _ :: _ -> 1;;

2017-03-26 追記

Camlのスタイルガイドに「最初のパターンにも書け」って書いてあったし、ギッハブとかで見てもどのマッチでも(コンストラクタとかも)書くのが普通って慣習っぽいな。


ところでStandard MLだと上みたいな余分な'|'は置けないみたいですね。datatypeは知らん。

fun f l = case l
  of [] => 0
   | _ => 1;
(* とか *)
fun f [] = 0
  | f _ = 1;

みたいに書く(っぽい)。

もしかしたら処理系によるのかも知れない(どこかで見たような気がするけど、OCamlと混同しているような気もする)。

一応SML/NJ, MLKit, Mltonは試したけど駄目だったので言語仕様的には不正だとは思うけど。

2009-08-17

[][]プログラミングの基礎 第 8 章 レコード 00:02 プログラミングの基礎 第 8 章 レコード - zrail (a.k.a. tobira17, h14i, ...) の Learning Log を含むブックマーク はてなブックマーク - プログラミングの基礎 第 8 章 レコード - zrail (a.k.a. tobira17, h14i, ...) の Learning Log

今回はレコードの話。

8.2 レコードの構文

注意。

# {namae = "asai"; tensuu = 70; seiseki = "B"}::
- : gakusei_t = {namae = "asai"; tensuu = 70; seiseki = "B"}

という例が最初に示されているけど、以下のように先に型の定義をしないとエラーが出るみたい。

# type gakusei_t = {name:string; score:int; grades:string};;
type gakusei_t = { name : string; score : int; grades : string; }
# {name = "asai"; score = 70; grades = "B"};;
- : gakusei_t = {name = "asai"; score = 70; grades = "B"}

本文中でも書いてあったんだけど、見落としてた。

どう書いても実行出来ないからおかしいなーと思ったら 8.5 節で型の定義はやるのね。

先に型定義の構文をやって欲しかったな。

8.3 レコードとパターンマッチ

タプル*1の場合とほぼ一緒。

パターン変数の決め方だけが違って、タプルの場合は構造的に?パターン変数の名前の割り当てが決まるけど、レコードは自分でフィールドにパターン変数の名前を決める。

8.4 そのほかの記法
  • フィールド名とパターン変数名は同じでも良い
  • パターン変数ではフィールドを省略出来る
  • 「レコード . フィールド」という書き方でも値は取り出せる( C の構造体みたいな感じ )
8.5 ユーザーによる型定義

型定義の例。

type gakusei_t = {
  name : string;
  score : int;
  grades : string;
};;

最後のフィールドの後のセミコロンは省略出来るみたい。

フィールド名の重複は不可。


まだ途中だけど今日はここまで。

*1:少年オッカムルの第五話で学んだ言葉!

2009-08-15

[][]プログラミングの基礎 第 7 章 組とパターンマッチ 21:53 プログラミングの基礎 第 7 章 組とパターンマッチ - zrail (a.k.a. tobira17, h14i, ...) の Learning Log を含むブックマーク はてなブックマーク - プログラミングの基礎 第 7 章 組とパターンマッチ - zrail (a.k.a. tobira17, h14i, ...) の Learning Log

少し間が空いたけど、再開する。

7 章から 9 章まではデータ構造の話みたい。

7.1 組の構文

2 つ以上の様々な型の値を組み合わせて「組」というデータ構造を表現出来る。

構造体みたいな感じ?

# (3.14, 2.71);;
- : float * float = (3.14, 2.71)
# (10, "str");;
- : int * string = (10, "str")
# (1, 'a', false);;
- : int * char * bool = (1, 'a', false)
# ((((3.13, 1), 1), 3), true);;
- : (((float * int) * int) * int) * bool = ((((3.13, 1), 1), 3), true)
7.2 パターンマッチ

パターンマッチの構文

matchwith
  パターン ->

注意。

  • パターン変数は互いに異なる変数でなくてはならない
  • パターン変数と与えられる組の型は同じでなくてはならない
# let test = (1, 2, 3);;
val test : int * int * int = (1, 2, 3)
# match test with
    (a, b) -> a + b;;
Characters 20-26:
      (a, b) -> a + b;;
      ^^^^^^
Error: This pattern matches values of type 'a * 'b
       but is here used to match values of type int * int * int
7.3 構造データに対するデザインレシピ

組を引数に取る関数はほぼ必ず組の要素を取り出す match 文から始まるはず。だからテンプレートを用意しておけば便利、という話。

2009-08-08

[][]第 6 章 12:50 第 6 章 - zrail (a.k.a. tobira17, h14i, ...) の Learning Log を含むブックマーク はてなブックマーク - 第 6 章 - zrail (a.k.a. tobira17, h14i, ...) の Learning Log

第 6 章はインタプリタからのエラーメッセージについての話。

6.2

大文字で始まる名前はコンストラクタ

6.3

OCaml インタプリタは型エラーの場合、実際は演算子の間違い ( ドットの付け忘れなど ) のときでも値 ( 変数や定数など ) が間違ってるというエラーを出す。

6.4

実行時エラーは例外のみ?

6.5

アルゴリズムの間違いなどの論理的なエラーについてはインタプリタは見つけられない。

言いかえると、論理的に正しいプログラムならアルゴリズムが間違っていても実行は出来る。

エラーというよりはバグかな?

2009-08-07

[][]第 5 章 条件分岐 10:42 第 5 章 条件分岐 - zrail (a.k.a. tobira17, h14i, ...) の Learning Log を含むブックマーク はてなブックマーク - 第 5 章 条件分岐 - zrail (a.k.a. tobira17, h14i, ...) の Learning Log

ようやく条件分岐まで来た。サボりまくってたので時間が掛かった。

5.2

条件分岐の構文

if 条件 then 式(真)else 式(偽)

C なんかの書き方とほとんど一緒。

注意。

  • 結果の2つの式は同じ型でなくてはならない
  • else は省略出来ない

トップレベル曰く、else を省略すると偽の場合の式が unit 型として評価される。

# let x = 3;;
val x : int = 3
# if x > 5 then true;;
Characters 14-18:
  if x > 5 then true;;
                ^^^^
Error: This expression has type bool but is here used with type unit
# if x > 5 then true else false;;
- : bool = false

問題 5.1 の (4) でビックリした。 OCaml って真偽値の大小比較が出来るのか!

大小関係はこうなってる。

true > false (* true のほうが大きい *)

真偽値の大小比較ってどんな場合に便利なんだろう。ちょっと思いつかないな。

今まで真偽値の大小比較が出来る言語って触ったことがない人は知らないとカルチャーショックを受ける言語仕様。

参考リンク

真偽値の大小比較:Rainy Day Codings:So-net blog

問題 5.1 の (5) もちょっとややこしいな。

条件分岐のあとに返ってくる値は評価されたあとの物が返ってくるみたい。

# if not(3 = 4) then 1 < 2 else 1 > 2;;
- : bool = true
5.4

if 文を他の式中に埋め込んで使う書き方。

本の例だと、まず普通?に書いた場合は

let kyuyo x = if x < 30 then kihonkyu + x * jikyu
                        else kihonkyu + x + yugu_jikyu

埋め込んで書いた場合は

let kyuyo x =
  kihonkyu + x * (if x < 30 then jikyu else yugu_jikyu)
5.5

条件分岐に対するデザインレシピの話。

関数定義のデザインレシピを拡張して考える。

  • 条件の場合分けを考える
  • 各場合について 1 つ 1 つテストする

新しい単項演算子が出てきた。プラスマイナスを反転させる演算子

0.0 -. x = -. x

という動作になっているみたい。


サポートページに問題 5.2 の解答例がないみたいなので貼っておく。

(* 目的:時間(24時間表示)を受け取って午前か午後かを判定する関数 *)
(* jikan : int -> string *)
let jikan time = if time > 12 then "pm"
                       else "am"
					   
(* test case *)
let test1 = jikan 5 = "am"
let test2 = jikan 16 = "pm"
let test3 = jikan 12 = "am"
let test4 = jikan 13 = "pm"

問題 5.3 もやったので一応載せてみる。

(* 目的: 誕生月 month と誕生日 day を受け取ったら 12 星座を返す関数 *)
(* seiza : int -> int -> string *)
let seiza month day =
    if month = 1 then if  1 <= day && day <= 19 then "Cap"
	         else if 20 <= day && day <= 31 then "Aqr"
                 else "none"
    else if month =  2 then if  1 <= day && day <= 18 then "Agr"
	               else if 19 <= day && day <= 29 then "Psc"
                       else "none"
    else if month =  3 then if  1 <= day && day <= 20 then "Psc"
	               else if 21 <= day && day <= 31 then "Ari"
                       else "none"
    else if month =  4 then if  1 <= day && day <= 19 then "Ari"
	               else if 20 <= day && day <= 30 then "Tau"
                       else "none"
    else if month =  5 then if  1 <= day && day <= 20 then "Tau"
	               else if 21 <= day && day <= 31 then "Gem"
                       else "none"
    else if month =  6 then if  1 <= day && day <= 22 then "Gem"
	               else if 23 <= day && day <= 30 then "Cnc"
                       else "none"
    else if month =  7 then if  1 <= day && day <= 22 then "Cnc"
	               else if 23 <= day && day <= 31 then "Leo"
                       else "none"
    else if month =  8 then if  1 <= day && day <= 22 then "Leo"
	               else if 23 <= day && day <= 31 then "Vir"
                       else "none"
    else if month =  9 then if  1 <= day && day <= 22 then "Vir"
	               else if 23 <= day && day <= 30 then "Lib"
                       else "none"
    else if month = 10 then if  1 <= day && day <= 23 then "Lib"
	               else if 24 <= day && day <= 31 then "Sco"
                       else "none"
    else if month = 11 then if  1 <= day && day <= 22 then "Sco"
	               else if 23 <= day && day <= 30 then "Sgr"
                       else "none"
    else if month = 12 then if  1 <= day && day <= 21 then "Sgr"
	               else if 22 <= day && day <= 31 then "Cap"
                       else "none"
    else "none"
	
(* test case *)
let test1 = seiza  8 17 = "Leo"
let test2 = seiza  3 20 = "Psc"
let test3 = seiza  6  1 = "Gem"
let test4 = seiza  2 28 = "Psc"
let test5 = seiza 11 25 = "Sgr"
let test6 = seiza 11 22 = "Sco"

何だか冗長な感じになっちゃったな。でも解答例もこんな感じだし、このままでいいか。

5.6

複雑な条件判定は if 文をネストすることで表現出来る。この辺は手続き型と一緒かな。

条件が複雑になって可読性が落ちそうなら、補助関数を作って見通しを良くする方法がある。

問題 5.4 から 5.7 は特に難しい部分はなかった。

5.7

条件分岐でも左結合で評価される。