OCamlの概要:基本[1]

(翻訳者からの序文:私は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の記述をよく見ると、いくつか質問があります。

次のセクションでこの質問に答えます。ここでは、Cで同じ関数を定義し(Javaでも同じように見えます)、さらにいくつかの質問を出したいと思っています。 同じ平均関数のCバージョンは次のとおりです。

ダブル
平均(ダブルa、ダブルb)
 {
   return(a + b)/ 2;
 }


OCamlのよりコンパクトなバージョンと比較してください。 ご質問をお寄せください。

OK、ここにいくつかの答えがあります:

これについては、次のセクションと章で詳しく説明します。

主な種類


種類値の範囲
int32ビットシステムでは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は最初の引数について文句を言うようになりました。

しかし、整数と浮動小数点数を追加する必要がある場合はどうでしょうか? (それらを変数if保存します)。 OCamlでは、直接キャストが必要です。

 (float_of_int i)+。  f ;;


float_of_intは、整数を受け取り、浮動小数点数を返す関数です。 int_of_floatchar_of_intint_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
   ;;

範囲はそれ自体を呼び出すことに注意してください。

letlet recの唯一の違いは、関数名のスコープです。 上の例の関数がletのみを使用して定義されている場合、 range関数を呼び出すと、今定義された関数ではなく、 rangeという既存の(以前に定義された)関数が検索されます。 letrecなし)を使用すると、前の定義のターミナルで値を再定義できます。 例:
 positive_sum ab = 
     let a = max a 0
    およびb = max b 0 in
     a + b


オーバーライドすると、以前の「バインディング」 abが関数定義から隠されます。 状況によっては、プログラマーは新しい変数を使用する( let a_pos = max a 0let a_pos = max a 0このアプローチを好むため、これにより古いバインディングが使用できなくなり、 bb最新の値のみがb

let recして関数を定義しても、 letに比べてパフォーマンスが変化することはありません。したがって、必要に応じて、常にlet recフォームを使用してCライクな言語に似た動作を得ることができます。

関数値の入力


型推論のおかげで、関数によって返される値の型を明示的に指定する必要はほとんどありません。 ただし、OCamlは関数の戻り値の型について多くの場合推測するため、このようなエントリの構文を知っておく必要があります。 arg 1arg 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)bb使用されたことがわかります。 .+関数は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).

Source: https://habr.com/ru/post/J108529/


All Articles