wme*_*yer 3 f# paul-graham accumulator
在我的追求,以了解更多F#,我试图执行一个"蓄电池发电机"由保罗·格雷厄姆描述这里.到目前为止,我最好的解决方案是完全动态输入:
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"
Run Code Online (Sandbox Code Playgroud)
我有三个问题:
x,则代码无法编译,因为编译器推断int -> objx的类型- 尽管acc注释为返回obj->obj.为什么这样,我可以避免吗?在我学习更多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))
Run Code Online (Sandbox Code Playgroud)
但是,数字塔在现实世界中实际上是无用的.除非你非常小心,否则试图从这些笨拙的挑战中学习,对你来说弊大于利.你应该先问问自己为什么我们不想要数字塔,不想打包而不想要运行时类型推广?
我们为什么不写:
let x = 1
let x = x + 5
ignore(3)
let x = float x + 2.3
Run Code Online (Sandbox Code Playgroud)
我们知道x每一步的类型.每个号码都是未装箱的.我们知道这段代码永远不会产生运行时类型错误......
我同意Jon的说法,这是一个非常人为的例子,它不是学习F#的好起点.但是,您可以使用静态成员约束来合理地关闭,而无需动态转换和反射.如果您将其标记为inline并使用以下命令添加转换两个参数float:
let inline acc x =
let x = ref (float x)
fun y ->
x := (float y) + !x
!x
Run Code Online (Sandbox Code Playgroud)
您将获得具有以下类型的函数:
val inline acc :
^a -> ( ^b -> float)
when ^a : (static member op_Explicit : ^a -> float) and
^b : (static member op_Explicit : ^b -> float)
Run Code Online (Sandbox Code Playgroud)
该函数接受任何两个可以显式转换为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
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
854 次 |
| 最近记录: |