【问题标题】:Built in f# operator to compose functions with the same input but different outputs?内置 f# 运算符来组合具有相同输入但不同输出的函数?
【发布时间】:2013-06-16 10:56:25
【问题描述】:

我了解 compose 运算符采用两个函数,它们都接受并返回相同的类型。例如(lhs:'a -> 'a) -> (rhs:'a -> 'a) -> 'a

我经常发现自己想要(lhs:'a -> 'b) -> (rhs:'c -> 'b) -> 'b 之类的东西,以防我对副作用感兴趣,而不是返回值 'b 可能是单位类型。仅当我连续两行将某些内容持久化到数据库时。

是否有内置函数或惯用的 F# 方式来执行此操作而无需编写类似的内容

let myCompose lhs rhs arg =
    lhs arg
    rhs arg

【问题讨论】:

  • 我从fsi 得到:val myCompose : lhs:('a -> unit) -> rhs:('a -> 'b) -> arg:'a -> 'b。你确定这就是你的意思吗?
  • 我不确定您对 << 运算符的看法。从 FSharp.Core 源 (prim-types.fsi),它的类型是:val inline (<<): func2:('T2 -> 'T3) -> func1:('T1 -> 'T2) -> ('T1 -> 'T3)

标签: f# functional-programming


【解决方案1】:

后向合成运算符(<<)定义为:

( << ) : ('b -> 'c) -> ('a -> 'b) -> 'a -> 'c`

加上两个谓词,实际上是一个函数,初始值'a返回'c,而'b的值在内部处理。

根据您提供的代码示例,让我假设您需要对两个谓词都应用一个参数。有几种方法可以做到这一点:

丢弃(第一个)谓词返回的值,而是返回原始参数。 WebSharper中存在这样的运算符:

let ( |>! ) x f = f x; x
// Usage:
let ret =
    x
    |>! f1
    |>! f2
    |> f3

我喜欢这种方法,因为:

  • 它不会使事情复杂化;每个函数应用都是原子的,代码看起来更具可读性;
  • 它允许在三个或更多谓词中进行链接,如上例所示;

在这种情况下,f 必须返回 unit,但您可以轻松解决此问题:

let ( |>!! ) x f = ignore(f x); x

将参数应用于两个谓词,返回结果元组,与您自己的示例完全相同。有OCaml这样的算子,很容易适应F#:

val (&&&) : ('a -> 'b) -> ('a -> 'c) -> 'a -> 'b * 'c

正如@JackP 所注意到的,&amp;&amp;&amp; 已经在 F# 中为其他目的而定义,所以让我们使用另一个名称:

/// Applying two functions to the same argument.
let (.&.) f g x = (f x, g x)

// Usage
let ret1, ret2 =
   x
   |> (f .&. g)

注意以上示例是针对函数应用的直接顺序。如果您需要以相反的顺序应用它们,则需要相应地修改代码。

【讨论】:

  • (&amp;&amp;&amp;) 是 F# 中的标准运算符(按位与)。 IMO最好选择其他东西;用一些完全不同的行为替换标准运算符感觉就像是“代码味道”。
【解决方案2】:

backward or reverse composition operator(&lt;&lt;) 不接受两个既接受又返回相同类型的函数;唯一的约束是要应用的第一个函数的输出类型必须与它所组成的函数的输入类型相同。根据 MSDN,函数签名是:

// Signature:
( << ) : ('T2 -> 'T3) -> ('T1 -> 'T2) -> 'T1 -> 'T3

// Usage:
func2 << func1

我不知道有什么内置的组合运算符可以按您的意愿工作,但是如果您在代码中经常使用这种模式并且拥有这样的运算符会简化您的代码,我认为定义您的组合运算符是合理的自己的。例如:

> let (<<!) func2 func1 arg = func1 arg; func2 arg;;

val ( <<! ) : func2:('a -> 'b) -> func1:('a -> unit) -> arg:'a -> 'b

或者,如果你知道这两个函数都将返回unit,你可以这样写来将输出类型限制为unit

> let (<<!) func2 func1 arg = func1 arg; func2 arg; ();;

val ( <<! ) : func2:('a -> unit) -> func1:('a -> unit) -> arg:'a -> unit

【讨论】:

  • 不知何故,func2func1 的类型被切换为 (&lt;&lt;!) 的第一个签名。
  • @kvb 好收获!它们现在已经修好了。
【解决方案3】:

要以任何所需的顺序组合任何f:'a-&gt;unit类型的函数,您可以简单地折叠他们的列表:

("whatever",[ printfn "funX: %A"; printfn "funY: %A"; printfn "funZ: %A" ])
||> List.fold (fun arg f -> f arg; arg )
|> ignore

进入 FSI

funX: "whatever"
funY: "whatever"
funZ: "whatever"
val it : unit = ()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-06-24
    • 2019-11-28
    • 1970-01-01
    • 2016-06-23
    • 2023-03-13
    • 2021-07-21
    • 1970-01-01
    • 2017-08-17
    相关资源
    最近更新 更多