功能组合是否依赖部分应用程序?
以下是我的理解:
观察以下函数调用重复的函数:
let updateCells (grid:Map<(int * int), Cell>) =
grid |> Map.toSeq
|> Seq.map snd
|> Seq.fold (fun grid c -> grid |> setReaction (c.X, c.Y)) grid
let mutable _cells = ObservableCollection<Cell>( grid |> Map.toSeq
|> Seq.map snd
|> Seq.toList )
let cycleHandler _ =
self.Cells <- ObservableCollection<Cell>( grid |> cycleThroughCells
|> Map.toSeq
|> Seq.map snd
|> Seq.toList )
如果您注意到,以下代码出现在所有三个函数中:
grid |> Map.toSeq
|> Seq.map snd
功能构成
在函数式编程中,我们可以将函数融合在一起,使它们成为一个函数。
为此,让我们从重复的函数序列中创建一个新函数:
let getCells = Map.toSeq >> Seq.map snd >> Seq.toList
现在,如果您细心,您会注意到我们在使用函数组合时不使用任何参数。因此,不使用网格值。这背后的原因是部分应用。
部分申请
我仍在学习所有这些函数式编程技术。但是,我的理解是,部分应用程序是函数式编程中的一种技术,它推迟了接受给定函数的完整参数集的需要。换句话说,部分应用是延迟接受给定函数的完整参数集的行为,其中期望最终客户端稍后将提供其余参数。至少,这是我的理解。
我们现在可以采用如下函数:
let updateCells (grid:Map<(int * int), Cell>) =
grid |> Map.toSeq
|> Seq.map snd
|> Seq.fold (fun grid c -> grid |> setReaction (c.X, c.Y)) grid
并将其重构为:
let updateCells (grid:Map<(int * int), Cell>) =
grid |> getCells
|> Seq.fold (fun grid c -> grid |> setReaction (c.X, c.Y)) grid
我对函数组合与部分应用程序耦合的想法是否准确?
最佳答案
泛型
实际上,如果你采取这种表达方式
let getCells = Map.toSeq >> Seq.map snd >> Seq.toList
并尝试将其编译为独立表达式,您将收到编译器错误:
error FS0030: Value restriction. The value 'getCells' has been inferred to have generic type val getCells : (Map<'_a,'_b> -> '_b list) when '_a : comparison
Either make the arguments to 'getCells' explicit or, if you do not intend for it to be generic, add a type annotation.
它适用于您的情况的原因是因为您使用的是
getCells
功能与 grid
,这意味着编译器推断它具有受约束的类型。为了保持通用性,您可以使用显式参数对其进行改写:
let getCells xs = xs |> Map.toSeq |> Seq.map snd |> Seq.toList
此表达式是
Map<'a,'b> -> 'b list when 'a : comparison
类型的有效独立表达式.免积分
>>
使用的样式函数组合运算符称为 point-free .它适用于部分应用程序,但并不完全相同。应用
然而,在这个例子中有一个偏函数应用的例子:
let getCells xs = xs |> Map.toSeq |> Seq.map snd |> Seq.toList
函数
snd
具有以下类型:'a * 'b -> 'b
它是一个接受单个参数的函数。
你也可以写上面的
getCells
没有部分应用 snd
的函数功能:let getCells xs = xs |> Map.toSeq |> Seq.map (fun x -> snd x) |> Seq.toList
请注意,不是传递给
Seq.map
的部分应用函数。 , 你可以传递一个 lambda 表达式。 getCells
function 仍然是由其他函数组成的函数,但不再依赖 snd
的部分应用.因此,部分(双关语)回答您的问题:函数组合不必依赖于部分函数组合。
curry
在 F# 中,函数默认是柯里化(Currying)的。这意味着所有函数都只接受一个参数,并返回一个值。有时(通常),返回值是另一个函数。
以
Seq.map
为例。功能。如果您使用一个参数调用它,则返回值是另一个函数:Seq.map snd
该表达式的类型为
seq<'a * 'b> -> seq<'b>
, 因为 Seq.map snd
的返回值是另一个功能。Eta减少
这意味着您可以对上述 lambda 表达式
fun x -> snd x
执行 Eta 归约。 , 因为 x
出现在表达式的两侧。结果很简单snd
,整个表达式变为let getCells xs = xs |> Map.toSeq |> Seq.map snd |> Seq.toList
如您所见,函数组合不需要部分应用,但它确实需要 让它更容易 .
公正
上述使用管道运算符 (
|>
) 的组合仍然依赖于函数 Map.toSeq
的部分应用。 , Seq.map
,等等。为了证明组合不依赖于部分应用,这里有一个“不偏不倚”(partial 的反义词?(双关语))替代方案:let getCells xs =
xs
|> (fun xs' -> Map.toSeq xs')
|> (fun xs' -> Seq.map (fun x -> snd x) xs')
|> (fun xs' -> Seq.toList xs')
请注意,此版本广泛使用 lambda 表达式而不是部分应用程序。
我不会以这种方式编写函数;我只包括这个替代方案来证明它是可以做到的。
关于f# - 功能组合是否依赖部分应用程序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36426315/