f# - 功能组合是否依赖部分应用程序?

标签 f# functional-programming

功能组合是否依赖部分应用程序?

以下是我的理解:

观察以下函数调用重复的函数:

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/

相关文章:

c# - 什么任务最适合用函数式编程风格完成?

scala - 到底是什么让 Option 成为 Scala 中的 monad?

javascript - 从二分查找函数返回值

css - 如何在 Suave web 中使用 css

json - 使用 Json.NET 将 F# 区分联合序列化为字符串

performance - 无点样式的性能含义

scala - 这个尾递归斐波那契函数的定义是尾递归的吗?

collections - list.map 和 list.collect 的区别

f# - F# : something like #elif 中的条件编译

EGC/文本元素上的 .NET String.Split