F# 中的箭头"proc"表示法



F#中是否有箭头的"proc"表示法的实现?在 Haskell 中,它看起来像这样:

mean2 :: Fractional a => Circuit a a
mean2 = proc value -> do
t <- total -< value
n <- total -< 1
returnA -< t / n

请注意proc关键字和-<符号。

理想情况下,这可能会以某种方式使用计算表达式,但我也对其他方法持开放态度。

我还没有看到任何与 F# 中等效的表示法。我认为原因是 F# 不尝试纯粹,因此没有proc-<正在解决的问题。

等效的 F# 只是:

let mean2 input = 
let mutable total = 0.
let mutable count = 0.
seq {
for i in input do
total <- total + i
count <- count + 1.
yield total / count
}

也许我误解了,proc关键字提供了比这个简单示例更多的功能。

更新整个答案:

这不是 Proc 等效的表示法,但你可以用 F# 编写:

let mean =
let t = total << id
let n = total << List.map (constant 1)
listFork t (/) n 

我将在下文中对此进行解释。

我更喜欢 J 的 fork 连接,它激发了下面的fork- 但请注意它并不完全相同 - 在 F# 中完成所有工作,据我所知,Arrows 会做,但这是由上面提到的 F# 库中的 Arrow 实现指导的,而不是查看 Haskell 用法(这与 Kliesli Arrow 是分开的, 我也使用这些,您为它们创建一个中缀运算符)。无论如何在 1 行中:

let fork g h f a = h (g a) (f a)
  • 根据需要与idfstsndfliptuple2uncurry等配合使用

无论如何,我将使用fork来创建平均值的方式,如问题中所述:

let average values = fork List.sum (/) List.length values

let average = fork List.sum (/) List.length 

运行平均版本,在此处使用列表。(fork可以提升到不同的集合,这里是listFork(如果扩展到列表集合,那么可以List.fork),以便与Haskell不同,F#通用,需要专用seqForkarrayFork组合器等)

更新:在风格上尽可能与Proc相似:

let total: int list -> float list = List.scan (+) 0 >>List.tail >> List.map float
let constant i = fun _ -> i
let fork g h f a = h (g a) (f a)
let inline listFork g h f  = fork g (List.map2 h) f 
let mean =
let t = total << id
let n = total << List.map (constant 1)
listFork t (/) n 
mean [0;10;7;8];;    
> 
val total : (int list -> float list)
val constant : i:'a -> 'b -> 'a
val fork : g:('a -> 'b) -> h:('b -> 'c -> 'd) -> f:('a -> 'c) -> a:'a -> 'd
val inline listFork :
g:('a -> 'b list) ->
h:('b -> 'c -> 'd) -> f:('a -> 'c list) -> ('a -> 'd list)
val mean : (int list -> float list)
val it : float list = [0.0; 5.0; 5.666666667; 6.25]

它显然不是Proc符号。现在我有了运行版本,我不知道这是否回答了您的"其他方法"完成点......

最终更新:

有了listFork,您只需 1 行即可使其更加无点:

let mean1 = listFork total (/) (List.map (fun _ -> 1) >> total)

在OP中链接的第一个示例中不清楚的是,total本身就是一个箭头,可以进行折叠或扫描的繁重提升。这就是 Arrows 的真正力量,因此它们在 FRP 中很有用,但这超出了我对fork的使用范围 - 它更侧重于无点、组合和管道。至少到现在为止,我会进一步考虑这个问题,但不是在这里!

您确实可以使用参数化的计算表达式,该表达式在箭头方向上有所作为。让我们首先看一下用于此类问题的基本构建块(同样可用于FRP):

  • Seq.scan用于在将文件夹应用于序列时捕获状态
  • Seq.zip重新组合两个序列

如下所示的朴素实现将迭代输入序列两次,因此可能需要考虑缓存它。

let inline total source =
Seq.scan (+) LanguagePrimitives.GenericZero source
|> Seq.skip 1
let inline mean source =
source
|> Seq.map (fun _ -> LanguagePrimitives.GenericOne)
|> total 
|> Seq.zip (total source) 
|> Seq.map ((<||) (/))

通过修改total函数以包含映射器,我们可以想出一个构建器,该生成器将输入序列迭代两次,并将其重新组合以用于最终除法运算符的应用。

let inline mapTotal (f : 'a->'b) source : seq<'b> =
Seq.scan (fun s t -> f t + s) LanguagePrimitives.GenericZero source
|> Seq.skip 1
type ZipMap<'a>(xs : seq<'a>) =
member __.Bind(a, f) = f (a xs)
member __.Return(op, x, y) = Seq.zip x y |> Seq.map ((<||) op)
let inline mean2 source =
ZipMap<_>(source){
let! t = mapTotal id
let! n = mapTotal (fun _ -> LanguagePrimitives.GenericOne)
return (/), t, n }
mean2 [0.; 10.; 7.; 8.]  // seq [0.0; 5.0; 5.666666667; 6.25]
mean2 [0; 10; 7; 8]      // seq [0; 5; 5; 6]

相关内容

  • 没有找到相关文章

最新更新