Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
F# F型蓄电池发电机#_F#_Accumulator - Fatal编程技术网

F# F型蓄电池发电机#

F# F型蓄电池发电机#,f#,accumulator,F#,Accumulator,为了了解更多的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>

为了了解更多的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"
开放系统
让acc(初始:obj):obj->obj=
let state=ref init
乐趣(x:obj)->
如果(!state).GetType()=typeof
&&x.GetType()=typeof then
状态:=(Convert.ToInt32(!state)+Convert.ToInt32(x)):>obj
其他的
状态:=(Convert.ToDouble(!state)+Convert.ToDouble(x)):>obj
!状态
做
设x:obj->obj=acc 1//此处需要类型注释
(x 5)|>忽略
printfn“%A”(x2)//打印“8”
printfn“%A”(x2.3)//打印“10.3”
我有三个问题:

  • 如果我删除
    x
    的类型注释,代码将无法编译,因为编译器推断x的类型
    int->obj
    ——尽管
    acc
    被注释为返回
    obj->obj
    。为什么会这样?我可以避免吗
  • 有什么办法改进这个动态类型的版本吗
  • 是否可以使用适当的静态类型来实现这一点?也许有成员限制?(在Haskell中是可能的,但在OCaml、AFAIK中是不可能的)

使用正确的静态类型绝对不可能实现这一点。你说你可以在哈斯克尔,但我不相信你

为了了解更多的F#,我尝试实现Paul Graham在这里描述的“累加器生成器”

此问题要求存在未指定的数字塔。Lisp恰好有一个,它恰好适合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
类型。每一个数字都是不装箱存储的。我们知道这段代码永远不会产生运行时类型错误…

我同意Jon的观点,这是一个非常人工的例子,不是学习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的任意两个参数。与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

使用静态类型尝试这样做的问题是,在保留左侧类型的同时,添加两个不同数量的可能不同的类型。正如乔恩·哈洛普所说,这在联合类型中是可能的。一旦定义了联合类型和相应的加法操作(如上所述),实际的累加器就非常简单了。我的实施:

module MyTest

type Numeric =
  | NInt of int
  | NFloat of float

  member this.Add(other : Numeric) : Numeric =
    match this with
      | NInt x ->
        match other with
          | NInt y -> NInt (x + y)
          | NFloat y -> NInt (x + (int y))
      | NFloat x ->
        match other with
          | NInt y -> NFloat (x + (float y))
          | NFloat y -> NFloat (x + y)

  override this.ToString() =
    match this with
      | NInt x -> x.ToString()
      | NFloat x -> x.ToString()

let foo (n : Numeric) =
  let acc = ref n
  fun i ->
    acc := (!acc).Add(i)
    !acc

let f = foo (NFloat 1.1)
(2 |> NInt |> f).ToString() |> printfn "%s"

你是对的。我对哈斯克尔不是很精通。我在accepted solutions()页面上看到Haskell,并假设它会起作用。现在我试过了,我发现Haskell解决方案似乎并没有真正满足要求(如果您在任何地方使用带浮点的生成函数,它会静态地决定一直使用浮点)。问题陈述需要
int->int->int
,但您的代码将返回
浮点
@Jon:Yes,这就是为什么我写了“在不使用动态强制转换的情况下相当接近”。在实践中,打破其中一个要求(一小部分)可能比使用装箱和拆箱更好。。。