2

F# についてさらに学ぶために、Paul Grahamがここで説明している「アキュムレータ ジェネレーター」を実装しようとしました。これまでのところ、私の最善の解決策は完全に動的に型付けされています。

open System

let acc (init:obj) : obj->obj=
  let state = ref init
  fun (x:obj) ->
    if (!state).GetType() = typeof<Int32>
       && x.GetType() = typeof<Int32> then
      state := (Convert.ToInt32(!state) + Convert.ToInt32(x)) :> obj
    else
      state := (Convert.ToDouble(!state) + Convert.ToDouble(x)) :> obj
    !state

do
  let x : obj -> obj = acc 1  // the type annotation is necessary here
  (x 5) |> ignore
  printfn "%A" (x 2)   // prints "8"
  printfn "%A" (x 2.3) // prints "10.3"

3 つの質問があります。

  • の型注釈を削除するxと、コンパイラはint -> objx の型を推測するため、コードはコンパイルに失敗しますが、accを返すように注釈が付けられていobj->objます。それはなぜですか?また、回避できますか?
  • この動的に型付けされたバージョンを改善するためのアイデアはありますか?
  • これを適切な静的型で実装することは可能ですか? 多分メンバーの制約がありますか?(Haskell では可能ですが、OCaml では不可能です)
4

4 に答える 4

8

F# についてさらに学ぶために、Paul Graham がここで説明しているように、「アキュムレータ ジェネレーター」を実装しようとしました。

この問題には、未指定の数値タワーの存在が必要です。Lisp にはたまたま 1 つがあり、Paul Graham の例にはそれで十分です。なぜなら、この問題は Lisp を人為的に見栄えよくするために特別に設計されたからです。

共用体型 ( などtype number = Int of int | Float of float) を使用するか、すべてをボックス化することにより、F# で数値タワーを実装できます。次のソリューションでは、後者のアプローチを使用しています。

let add (x: obj) (y: obj) =
  match x, y with
  | (:? int as m), (:? int as n) -> box(m+n)
  | (:? int as n), (:? float as x)
  | (:? float as x), (:? int as n) -> box(x + float n)
  | (:? float as x), (:? float as y) -> box(x + y)
  | _ -> failwith "Run-time type error"

let acc x =
  let x = ref x
  fun (y: obj) ->
    x := add !x y
    !x

let x : obj -> _ = acc(box 1)
do x(box 5)
do acc(box 3)
do printfn "%A" (x(box 2.3))

ただし、数値タワーは現実の世界ではほとんど役に立ちません。十分に注意しない限り、この種のつまらない課題から学ぼうとすると、益よりも害が大きくなります。なぜ数値タワーを望まないのか、ボックス化したくないのか、ランタイム タイプの昇格を望まないのか、自問自答しておく必要があります。

なぜ私たちはただ書かなかったのですか:

let x = 1
let x = x + 5
ignore(3)
let x = float x + 2.3

x私たちはすべてのステップでのタイプを知っています。すべての番号はボックス化されていない状態で保管されます。このコードが実行時型エラーを生成することは決してないことがわかっています...

于 2010-09-05T11:21:37.597 に答える
6

これは非常に人為的な例であり、F# を学習するための良い出発点ではないというジョンの意見に同意します。ただし、静的メンバー制約を使用して、動的キャストやリフレクションなしで適度に近づけることができます。としてマークしてinline追加すると、次を使用して両方のパラメーターを変換しますfloat

let inline acc x = 
  let x = ref (float x)
  fun y ->
    x := (float y) + !x
    !x

次のタイプの関数を取得します。

val inline acc :
   ^a -> ( ^b -> float)
    when  ^a : (static member op_Explicit :  ^a -> float) and
          ^b : (static member op_Explicit :  ^b -> float)

この関数は、明示的に float に変換できる任意の 2 つの引数を取ります。LISP バージョンと比較した唯一の制限 (推測) は、常に float を返すことです (利用可能な最も一般的な数値型として)。次のように書くことができます:

> acc 1 2;;            // For two integers, it returns float
val it : float = 3.0
> acc 1 2.1;;          // integer + float
val it : float = 3.1
> acc 1 "31";;         // It even works with strings!
val it : float = 32.0
于 2010-09-05T11:45:01.407 に答える
3

これを適切な静的型で実装することは絶対に不可能です。あなたはHaskellでできると言っていますが、私はあなたを信じていません.

于 2010-09-05T00:39:08.900 に答える