F# 中的箭头“proc”符号

问题描述

在 F# 中是否有 "proc" notationarrows 实现?在 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 Arrows 不同,我也使用它们,您为它们创建了一个中缀运算符)。无论如何在 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)所以 F# 与 Haskell 不同,需要专门的 {{1} },seqFork 组合子等)

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

arrayFork

这显然不是 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] ,您只需使用 1-liner 即可使这更无任何点:

listFork

在 OP 中链接的第一个示例中不清楚的是 let mean1 = listFork total (/) (List.map (fun _ -> 1) >> total) 本身是一个箭头,它可以完成折叠或扫描的繁重工作。这就是 Arrows 的真正力量,因此它们在 FRP 中很有用,但这已经(或曾经)超出了我使用 total 的范围——它更专注于无点、组合和管道。至少到现在为止,我会进一步考虑这一点,但不会在这里!

,

你确实可以使用一个参数化的计算表达式,它在箭头的方向上有一定的作用。让我们先看看用于此类问题的基本构建块(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]

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...