函数组合是否依赖于部分应用程序?

时间:2016-04-05 12:24:35

标签: 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

我对功能构成与部分应用程序的关联是否准确?

3 个答案:

答案 0 :(得分:11)

泛型

实际上,如果你采用表达式

let getCells = Map.toSeq >> Seq.map snd >> Seq.toList

并尝试将其编译为独立表达式,您将收到编译器错误:

  

错误FS0030:值限制。已推断值'getCells'具有泛型类型       val getCells :(地图&lt;'_ a,'_ b&gt; - &gt;'_b列表)当'_a:比较
时   要么将'getCells'的参数显式化,要么如果你不打算将它作为泛型,那么添加一个类型注释。

它适用于您的情况的原因是因为您使用 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函数仍然是由其他函数组成的函数,但它不再依赖snd的部分应用。

因此,部分(双关语)回答你的问题:功能组合不必依赖于部分功能组合。

柯里

在F#中,默认情况下,函数 curried 。这意味着所有函数都采用一个参数,并返回一个值。有时(经常),返回值是另一个函数。

例如,考虑Seq.map函数。如果使用一个参数调用它,则返回值为另一个函数:

Seq.map snd

此表达式的类型为seq<'a * 'b> -> seq<'b>,因为Seq.map snd的返回值是另一个函数。

Eta减少

这意味着您可以对上述lambda表达式fun x -> snd x执行 Eta reduction ,因为x出现在表达式的两侧。结果只是snd,整个表达式变为

let getCells xs = xs |> Map.toSeq |> Seq.map snd |> Seq.toList

正如您所看到的,部分应用程序不是必要的用于功能组合,但使其更容易

铁面

使用管道运算符(|>)的上述组合仍然依赖于函数Map.toSeqSeq.map等的部分应用。为了证明构图不依赖于部分应用,这里是一个“公正”(与部分?(双关语)相反)的替代方案:

let getCells xs =
    xs
    |> (fun xs' -> Map.toSeq xs')
    |> (fun xs' -> Seq.map (fun x -> snd x) xs')
    |> (fun xs' -> Seq.toList xs')

请注意,此版本广泛使用lambda表达式而不是部分应用程序。

我不会以这种方式撰写功能;我只包括这个替代方案来证明它可以完成。

答案 1 :(得分:8)

组成取决于一等函数,而不是部分应用程序。

实施组合需要的是:

  • 函数必须能够作为参数并作为返回值返回
  • 功能签名必须是有效类型(如果您希望组合强类型)

部分应用程序为合成创建了更多机会,但原则上您可以在没有它的情况下轻松定义函数合成。

例如,C#没有部分应用*,但只要签名匹配,您仍然可以将两个函数组合在一起:

Func<a, c> Compose<a, b, c>(this Func<a, b> f, 
                            Func<b, c> g) 
{
    return x => g(f(x));
}

只是>>,语法更加清晰:f.Compose(g)

然而,构图和部分应用之间存在一个有趣的联系。 >>运算符的定义是:

let (>>) f g x = g(f(x))

所以,当您编写foo >> bar时,您确实部分应用 (>>)函数,即省略x参数以获取{{1}部分结果。

但是,正如我上面所说,这并非严格必要。上面的fun x = g(f(x))函数相当于F#&#39; Compose运算符,并不涉及任何部分应用程序; lambdas以稍微冗长的方式扮演同样的角色。

*除非你手动实现它,否则没人做。即而不是写作

>>

你必须写

string foo(int a, int b) 
{ 
    return (a + b).ToString(); 
}

然后你就可以像在F#中一样分别传递每个参数。

答案 2 :(得分:1)

这更多是评论而不是答案,因为评论很重要。

我从堆栈中提取参数的方法是使用fun关键字。

// Standard function with arguments
let add1 x y  = x + y

// Function defining no arguments.  
// I think of the arguments x and y as being on the stack.
let add2 = (+)

// Function defining no arguments.  
// I think of the arguments x and y as being on the stack 
// and being given the names x and y.
let add3 = fun x y -> x + y

let result1 = add1 1 2
printfn "add1: %A" result1

let result2 = add2 1 2
printfn "add2: %A" result2

let result3 = add3 1 2
printfn "add3: %A" result3

发送到F#interactive产生

val add1 : x:int -> y:int -> int

val add2 : (int -> int -> int)

val add3 : x:int -> y:int -> int

val result1 : int = 3
add1: 3
val it : unit = ()

val result2 : int = 3
add2: 3
val it : unit = ()

val result3 : int = 3
add3: 3
val it : unit = ()

以及使用function关键字

的另一个示例
let charType =
    function
    | n when n >= '0' && n <= '9' -> "number"
    | l when l >= 'a' && l <= 'z' -> "lower case"
    | u when u >= 'A' && u <= 'Z' -> "upper case"
    | _ ->  "other"

let result4 = charType '0'
printfn "charType '0': %A" result4