(翻訳者からの序文:私はokamlを学ぶために座ったが、初心者向けガイドのロシア語への翻訳がないことがわかった。このギャップを埋める)。
基本
コメント
OCamlのコメントは、次のような文字(*および*)で示されます。
(* *)
(*これはコメントです
いくつかによって
行。
*)
言い換えれば、OCamlのコメントはCのコメント(
/* ... */
)に非常に似ています。
現在、1行のコメントはありません(Pearlの
#...
やC99 / C ++ / Javaの
// ...
)。
## ...
を使用する可能性について
## ...
かつて議論されましたが、オカムロフの仲間は将来この機会を追加することを強くお勧めします(ただし、優れた編集者は現在でも単一行コメントを使用できます)。
OCamlのコメントはネストされているため、コメントを使用してコードの一部に非常に簡単にコメントできます。
(* This code is broken ...
(* Primality test. *)
let is_prime n =
(* note to self: ask about this on the mailing lists *) XXX;;
*)
関数呼び出し
元の文字列sと数nを取り、繰り返し文字列sのn倍で構成される新しい文字列を返す関数を作成し、繰り返し呼び出したとします。
ほとんどのCライクな言語では、関数呼び出しは次のようになります。
repeated ("hello", 3) /* this is C code */
これは、「2つの引数で繰り返し関数を呼び出す、最初の引数は文字列hello、2番目の引数は数字3」を意味します。
他の関数型プログラミング言語と同様に、OCamlでは、関数呼び出しの記録と括弧の使用が大幅に異なるため、多くのエラーが発生します。 OCamlに記録された同じ呼び出しの例を次に示します。
repeated "hello" 3 (* this is OCaml code *)
です。
引数の間に角括弧やカンマがないことに注意してください。
OCamlの観点から
repeated ("hello", 3)
表現
repeated ("hello", 3)
は理にかなっています。 これは、「2つの要素のペア構造である1つの引数で繰り返し関数を呼び出す」ことを意味します。 もちろん、これはエラーにつながります。なぜなら、繰り返される関数は1つではなく2つの引数を予期し、最初の引数はペアではなく文字列である必要があるためです。 ただし、ペア(タプル)の詳細については特に説明しません。 代わりに、関数に引数を渡すときに角かっことコンマを使用するのは間違いです。
別の関数prompt_stringを考えます。この関数は、招待テキストを含む文字列を受け取り、ユーザー入力を返します。 この関数の結果を繰り返し関数に渡したいです。 CおよびOCamlのバージョンは次のとおりです。
/ * Cコード:* /
繰り返される(prompt_string( "Name please:")、3)
(* OCamlコード:*)
繰り返されます(prompt_string "Name please:")3
括弧と欠落しているコンマをよく見てください。 OCamlバージョンでは、別の関数を呼び出した結果である繰り返し関数の最初の引数が括弧で囲まれています。 一般的な規則:「関数の引数を囲むのではなく、関数呼び出しを囲む」。 以下に例を示します。
f 5(g "hello")3(* fには3つの引数があり、gには1つの引数があります*)
f(g 3 4)(* fには1つの引数があり、gには2つの引数があります*)
#繰り返し( "hello"、3);; (* OCamlはエラーをスローします*)
この式にはstring * int型がありますが、ここではstring型で使用されます
関数定義
おなじみの言語で関数(またはJavaの静的メソッド)を定義する方法をすべて知っているとします。 しかし、OCamlでどのように行うのでしょうか?
OCamlの構文はエレガントで簡潔です。 以下は、2つの浮動小数点引数を取り、平均を計算する関数です。
平均ab =
(a +。b)/。 2.0 ;;
OCamlのトップレベルでこれを設定します。 (このため、Unixでは、ocamlと入力するだけです)。 [注 あたり ubuntu / debian sudo aptitude install ocamlの場合、suse / centos / fedoraの場合-sudo yum install ocaml]。 表示されます:
#平均ab =
(a +。b)/。 2.0 ;;
val平均:float-> float-> float = <fun>
関数の定義とOCamlの記述をよく見ると、いくつか質問があります。
- コードで余分なセミコロンは何をしますか?
- それはすべて、
float -> float -> float
とはどういう意味ですか?
次のセクションでこの質問に答えます。ここでは、Cで同じ関数を定義し(Javaでも同じように見えます)、さらにいくつかの質問を出したいと思っています。 同じ平均関数のCバージョンは次のとおりです。
ダブル
平均(ダブルa、ダブルb)
{
return(a + b)/ 2;
}
OCamlのよりコンパクトなバージョンと比較してください。 ご質問をお寄せください。
- OCamlバージョンで変数タイプ
a
とb
を設定しなかったのはなぜですか? OCamlはどのように型を定義しましたか? (そして、一般的に、OCaml は型を知っていますか、それとも完全に動的に型付けされた言語ですか?) - Cでは、数値2は暗黙的にdoubleにキャストされますが、OCamlでも同じことができますか?
- OCamlで書かれたreturnステートメントの類似物はどうですか?
OK、ここにいくつかの答えがあります:
- OCamlは強力な静的型付けを備えた言語です(つまり、Pearlのint、float、string間のキャストのような動的キャストはありません)
- OCamlは型推論を使用して型を決定するため、手動で行う必要はありません。 上記の例のように、OCamlのトップレベルでコードを入力すると、OCamlは関数の型推論を報告します
- OCamlは暗黙的なキャストを実装しません。 floatが必要な場合、
2
は整数であるため、明示的に2.0
記述する必要があります[約。 transl .:英語では、小数部分は整数ポイントから分離され、型は文字通り「浮動小数点数」と呼ばれるため、OCamlでは、整数部分と小数部分を分離するためにポイントが使用されます。 OCaml は 、int、float、string、またはその他の型の間で自動的に変換しません 。 - OCamlの型推論の副作用として、関数(演算子を含む)はオーバーロードできません。 OCamlは、 整数を追加する操作として
+
を定義します 。 浮動小数点数を追加するには、 +.
使用します+.
(プラス記号の後のポイントに注意)。 同様に、 -.
が使用され-.
、 *.
、 /.
他の浮動小数点演算用。 - OCamlにはreturnステートメントがありません。関数の最後の式が関数の値として自動的に使用されます。
これについては、次のセクションと章で詳しく説明します。
主な種類
種類 | 値の範囲 |
---|
int | 32ビットシステムでは31ビット符号付き整数、64ビットプロセッサを搭載したシステムでは63ビット符号付き整数 |
浮く | Cのdoubleと同等の倍精度浮動小数点数(IEEE) |
ブール | ブール、true / false |
チャー | 8ビット文字 |
ひも | ひも |
OCamlはintビットの1つを使用して、自動メモリ管理(ガベージコレクション)用のデータを格納します。 これが、int次元が32ビットではなく31ビット(64ビットシステムでは63ビット)である理由です。 通常の使用では、いくつかの特定の場合を除いて、これは問題ではありません。 たとえば、ループで何かをカウントする場合、OCamlは反復回数を2ではなく10億に制限します。これは問題ではありません。任意の言語でint制限に近いものをカウントする場合、特別なモジュールを使用して大きな数で作業する必要があるためです(OCamlのNatおよびBig_intモジュール)。 ただし、32ビット値(たとえば、暗号コードやネットワークスタックコード)を処理するために、OCamlはプラットフォームの整数
nativeint
数に対応する
nativeint
型を提供します。
OCamlには符号なし整数に対応する基本型はありませんが、
nativeint
を使用して取得できます。 私の知る限り、OCamlは単精度浮動小数点をサポートしていません。
OCamlでは、
char
型はテキスト内の文字を表すために使用されます。 残念ながら、
char
型は、マルチバイトエンコーディングの形式でもUTF-8の形式でも、Unicodeをサポートしていません。 これはOCamlの重大な欠陥であり、修正する必要がありますが、現時点では、ユニコードサポート用の広範なライブラリが役立つはずです。
文字列は単なるバイトのシーケンスではありません。 独自のより効率的な保管方法を使用します。
unit
型は、Cの
void
型とある程度似てい
void
が、後で説明します。
明示的な型指定と暗黙的な型指定
Cライクな言語では、特定の状況下で全体が浮動小数点数に変わります。 たとえば、
1 + 2.5
と記述すると、最初の引数(整数)が浮動小数点にキャストされ、結果も浮動小数点になります。 これは、明示的に
((double)1)+2.5
記述することで実現できます。
OCamlは暗黙の型変換を決して行いませんOCaml
1 + 2.5
では型エラーです。 加算演算子
+
は2つの整数引数を必要とし、引数の1つが浮動小数点数の場合、エラーを報告します。
#1 + 2.5 ;;
^^^
この式はfloat型ですが、ここではint型で使用されます
(特定の言語、「フランス語からの翻訳」では、エラーメッセージは「浮動小数点をここに入れて、全体を期待した」という意味です)[約。 transl .: OCamlはフランス人によって開発され、著者はフランス語から英語へのエラーメッセージの翻訳に失敗したことを楽しんでいます]。
2つの浮動小数点数を追加するには、別の演算子
+.
(ポイントに注意を払ってください)。
OCamlは整数を浮動小数点にキャストしないため、これもエラーです。
#1 +。 2.5 ;;
^
この式はint型ですが、ここではfloat型で使用されます
OCamlは最初の引数について文句を言うようになりました。
しかし、整数と浮動小数点数を追加する必要がある場合はどうでしょうか? (それらを変数
i
と
f
保存します)。 OCamlでは、直接キャストが必要です。
(float_of_int i)+。 f ;;
float_of_int
は、整数を受け取り、浮動小数点数を返す関数です。
int_of_float
、
char_of_int
、
int_of_char, string_of_int
ような、同様のアクションを実行するこのような関数が
int_of_char, string_of_int
ます。 ほとんどの場合、彼らは彼らに期待されることを行います。
intからfloatへの変換は非常に一般的であるため、
float_of_int
には短いエイリアスがあります。 上記の例は次のように書くことができます
float i +。 f ;;
(Cとは異なり、OCamlでは、関数と型の両方に同じ名前を付けることができます)。
明示的なキャストと暗黙的なキャストのどちらが良いですか?
明示的なキャストは見苦しく、退屈な作業だと思われるかもしれません。 いくつかの点であなたは正しいですが、明示的なキャストを支持する少なくとも2つの議論があります。 まず、OCamlは明示的な型変換を使用して型推論を有効にします(以下を参照)。また、型推論は非常に優れた時間節約機能であり、明示的に入力するときに余分なボタンを押すよりも明らかに重要です。 第二に、Cプログラムを少なくとも1回デバッグした場合、(a)暗黙的な型付けはエラーを見つけるのを難しくします。(b)ほとんどの場合、座って暗黙的な型付けが機能した場所を見つけようとします。 明示的な型指定の要件は、デバッグに役立ちます。 第三に、一部の型キャスト(特に整数<->浮動小数点)は実際には非常に高価な操作です。 あなたは暗黙のタイピングでそれらを隠すことによってあなた自身を傷つけます。
通常および再帰関数
Cライクな言語とは異なり、通常の
let
代わりに
let rec
式を使用して明示的に指定しない限り、関数は再帰を許可しません
let
範囲をab =とする
a> bの場合、[]
それ以外の場合:::範囲(a + 1)b
;;
範囲はそれ自体を呼び出すことに注意してください。
let
と
let rec
の唯一の違いは、関数名のスコープです。 上の例の関数が
let
のみを使用して定義されている場合、
range
関数を呼び出すと、今定義された関数ではなく、
range
という既存の(以前に定義された)関数が検索されます。
let
(
rec
なし)を使用すると、前の定義のターミナルで値を再定義できます。 例:
positive_sum ab =
let a = max a 0
およびb = max b 0 in
a + b
オーバーライドすると、以前の「バインディング」
a
と
b
が関数定義から隠されます。 状況によっては、プログラマーは新しい変数を使用する(
let a_pos = max a 0
)
let a_pos = max a 0
このアプローチを好むため、これにより古いバインディングが使用できなくなり、
b
と
b
最新の値のみが
b
。
let rec
して関数を定義しても、
let
に比べてパフォーマンスが変化することはありません。したがって、必要に応じて、常に
let rec
フォームを使用してCライクな言語に似た動作を得ることができます。
関数値の入力
型推論のおかげで、関数によって返される値の型を明示的に指定する必要はほとんどありません。 ただし、OCamlは関数の戻り値の型について多くの場合推測するため、このようなエントリの構文を知っておく必要があります。
arg 1
、
arg 2
、...
arg n
rettype
を返す関数
f
rettype
コンパイラは次を出力します。
f:arg1-> arg2-> ...-> argn-> rettype
矢印を使用した構文は異常に見えますが、その後、いわゆる派生関数(カリー化)について説明します。 いくつか例を挙げましょう。
repeated
関数は文字列を受け取り、整数は文字列を返します。 そのタイプは次のとおりです。
繰り返し:文字列-> int->文字列
2つの浮動小数点数を取り、1つの浮動小数点数を返す
average
関数は、次のように記述されます。
平均:フロート->フロート->フロート
標準のOCaml関数
int_of_char
次のとおりです。
int_of_char:char-> int
関数が何も返さない場合(CおよびJavaの場合は
void
)、タイプ
unit
返すと記述します。 例えば、これは
fputs
関数の類似物がOCamlでどのように見えるかです:
output_char:out_channel-> char-> unit
多相関数
少し奇妙になりました。 引数として
何かを取る関数については
どうですか? 以下は、1つの引数をとるが、それを無視して常に数値3を返す異常な関数の例です。
let give_me_a_three x = 3 ;;
この関数はどのタイプですか? OCamlは、「あなたの魂が望むものは何でも」という意味の特別な代替物を使用します。 これは、単一引用符とそれに続く文字です。 上記の関数のタイプは次のように記述されます。
give_me_a_three: 'a-> int
ここで、
'a
は実際には「あらゆるタイプ」を意味します。 たとえば、この関数を
give_me_a_three "foo"
または
give_me_a_three 2.0
として呼び出すことができます。 OCamlの観点からは、両方のオプションは等しく正しいでしょう。
ポリモーフィック関数が有用な理由は明確ではありませんが、実際には非常に有用であり、非常に一般的です。 後で説明します。 (ヒント:ポリフィズムはC ++のパターンまたはJavaのジェネリックのようなものです)。
型推論
このチュートリアルのテーマは、関数型言語には多くのクールな機能が含まれ、OCamlはすべてのクールな機能を1か所に集めた言語であり、実際のプログラマーにとって実際に非常に役立つという考えです。 しかし奇妙なことに、これらの便利な機能のほとんどは「関数型プログラミング」とは無関係です。 実際、私は最初のCool Ficheに行きましたが、関数型プログラミングが「関数型」と呼ばれる理由についてはまだ何も言いませんでした。 いずれにせよ、ここに最初のクールな機能があります:型推論。
簡単に言えば、関数と変数の型を宣言する必要はありません。OCamlが型を宣言するからです。
さらに、OCamlは、複数のファイル間でも、すべてのタイプチェックを実行します。
しかし、OCamlは実用的な言語であり、そのために、タイプ管理システムにバックドアが含まれているため、これらのまれなケースでは理にかなっている場合にタイプチェックをバイパスできます。 ほとんどの場合、OCamlの実際の達人だけが型チェックの回避策を必要とします。
OCamlのトップレベルで導入された
average
関数に戻りましょう。
#平均ab =
(a +。b)/。 2.0 ;;
val平均:float-> float-> float = <fun>
Mirabile dictu! OCamlはすべてを自分で行い、関数が2つの浮動小数点数を取り、浮動小数点数を返すことを決定しました。
どうやって? 最初に、式
(a + . b)
で
b
と
b
使用されたことがわかります。
.+
関数は2つの浮動小数点引数を必要とすることが知られているため、単純な演de法で
a b
float
.
, /.
float, , , average
, average
float. :
average : float -> float -> float
, , , . , , , NullPointerException ClassCastException (, , , , Perl).
ことを推測でき
a b
float
.
, /.
float, , , average
, average
float. :
average : float -> float -> float
, , , . , , , NullPointerException ClassCastException (, , , , Perl).
a b
float
.
, /.
float, , , average
, average
float. :
average : float -> float -> float
, , , . , , , NullPointerException ClassCastException (, , , , Perl).
a b
float
.
, /.
float, , , average
, average
float. :
average : float -> float -> float
, , , . , , , NullPointerException ClassCastException (, , , , Perl).
a b
float
.
, /.
float, , , average
, average
float. :
average : float -> float -> float
, , , . , , , NullPointerException ClassCastException (, , , , Perl).