`fun` lambda 表达式有速记语法吗?

Posted

技术标签:

【中文标题】`fun` lambda 表达式有速记语法吗?【英文标题】:do `fun` lambda expressions have shorthand syntax? 【发布时间】:2022-01-20 02:15:42 【问题描述】:

我想确保我不会错过可读但简洁的 F# 语法:我在下面使用 fun 是否过于冗长?

let toCamelCase word indexes =
    let mapping i c =
        match (indexes |> List.contains i) with
        | true                      -> Char.ToUpper(c)
        | _ when Char.IsUpper(c)    -> Char.ToLower(c)
        | _                         -> c

    word |> String.mapi mapping

[
    ("fsharP", [0; 1])
    ("nAtiveinterop", [0; 6])
    ("taskbuildereXtensions", [0; 4; 11])
    ("microsoftword", [0; 9])
]
|> List.map (fun (word, indexes) -> (word, indexes) ||> toCamelCase)

另外,请告诉我上面代码中的其他地方是否可以改进

【问题讨论】:

如果让toCamelCase 采用元组参数,则根本不需要 lambda 表达式 这是一个单一的功能?在这种情况下,下半部分的缩进不正确。 您可以删除除最后一行之外的所有括号。这将消除大量噪音,从而提高可读性。 【参考方案1】:

在某些函数式语言中,uncurry 函数很常见:

let uncurry f (a,b) = f a b

那你可以改写|> List.map (uncurry toCamelCase)

或者,您可以稍微简化一下您现在拥有的内容:

|> List.map (fun pair -> pair ||> toCamelCase)

【讨论】:

【参考方案2】:

想添加一个不太关注currying/uncurrying的答案。这涉及到其他人已经在这里提出的一些观点,但希望更多细节会有所帮助。

关于您的问题,如果您想传入lambda function,则需要fun 关键字。您可以通过更改函数签名来避免这种情况:

let toCamelCase (word, indexes) =
    let mapping i c =
        match (indexes |> List.contains i) with
        | true                      -> Char.ToUpper(c)
        | _ when Char.IsUpper(c)    -> Char.ToLower(c)
        | _                         -> c

    word |> String.mapi mapping

[
    ("fsharP", [0; 1])
    ("nAtiveinterop", [0; 6])
    ("taskbuildereXtensions", [0; 4; 11])
    ("microsoftword", [0; 9])
]
|> List.map toCamelCase

如果您使用的是一个函数,无论出于何种原因,您都无法进行此更改,您可以创建一个中间辅助函数(这实际上是手动取消柯里化):

let toCamelCase word indexes =
    let mapping i c =
        match (indexes |> List.contains i) with
        | true                      -> Char.ToUpper(c)
        | _ when Char.IsUpper(c)    -> Char.ToLower(c)
        | _                         -> c

    word |> String.mapi mapping

let toCCHelper (word, indexes) =
    toCamelCase word indexes

[
    ("fsharP", [0; 1])
    ("nAtiveinterop", [0; 6])
    ("taskbuildereXtensions", [0; 4; 11])
    ("microsoftword", [0; 9])
]
|> List.map toCCHelper

您可以选择稍微简化您的 lambda 函数而不做其他任何更改。这是有效的,因为双管道 (||>) 将为您解构元组输入:

let toCamelCase word indexes =
    let mapping i c =
        match (indexes |> List.contains i) with
        | true                      -> Char.ToUpper(c)
        | _ when Char.IsUpper(c)    -> Char.ToLower(c)
        | _                         -> c

    word |> String.mapi mapping

[
    ("fsharP", [0; 1])
    ("nAtiveinterop", [0; 6])
    ("taskbuildereXtensions", [0; 4; 11])
    ("microsoftword", [0; 9])
]
|> List.map (fun x -> x ||> toCamelCase)

还有一些括号是不需要的,所以这取决于你的喜好。这里有一些清理以及一些逻辑更改供您考虑:

let toCamelCase (word, indexes) =
    let mapping i c =
        // You can omit parens here  if you want:
        match indexes |> List.contains i with
        // This logic might be easier to maintain, no parens needed here:
        | true  -> Char.ToUpper c
        | false -> Char.ToLower c

    word |> String.mapi mapping

// The parens here are also optional when you're putting 1 entry per line
// (1 tuple being 1 entry in this case):
[
    "fsharP", [0; 1]
    "nAtiveinterop", [0; 6]
    "taskbuildereXtensions", [0; 4; 11]
    "microsoftword", [0; 9]
]
|> List.map toCamelCase

【讨论】:

【参考方案3】:

这是我想出的版本。我更喜欢xs4,并且只做内联的所有事情,完全不用map。如果函数名称很长,可能是xs5

最重要的是,为了便于阅读,我会添加空格来创建表格视图。但这可能是一个关心可读性的老 Perl 程序员的一个旧习惯,并且在任何其他语言中都没有被广泛使用。也许所有其他语言都认为它们已经可读了?

(* Original *)
let xs1 =
    [
        "fsharP"               , [0; 1]
        "nAtiveinterop"        , [0; 6]
        "taskbuildereXtensions", [0; 4; 11]
        "microsoftword"        , [0; 9]
    ]
    |> List.map (fun (word, indexes) -> (word, indexes) ||> toCamelCase)

(* Remove useless piping *)
let xs2 =
    [
        "fsharP"               , [0; 1]
        "nAtiveinterop"        , [0; 6]
        "taskbuildereXtensions", [0; 4; 11]
        "microsoftword"        , [0; 9]
    ]
    |> List.map (fun (word, indexes) -> toCamelCase word indexes)

(* If you use piping, then like this *)
let xs3 =
    [
        "fsharP"               , [0; 1]
        "nAtiveinterop"        , [0; 6]
        "taskbuildereXtensions", [0; 4; 11]
        "microsoftword"        , [0; 9]
    ]
    |> List.map (fun wi -> wi ||> toCamelCase)

(* toCamelCase part of list *)
let xs4 = [
    toCamelCase "fsharP"                [0; 1]
    toCamelCase "nAtiveinterop"         [0; 6]
    toCamelCase "taskbuildereXtensions" [0; 4; 11]
    toCamelCase "microsoftword"         [0; 9]
]

(* you can create a shortcut for the function *)
let xs5 =
    let f = toCamelCase
    [
        f "fsharP"                [0; 1]
        f "nAtiveinterop"         [0; 6]
        f "taskbuildereXtensions" [0; 4; 11]
        f "microsoftword"         [0; 9]
    ]

(* Use map2 *)
let xs6 =
    List.map2
        toCamelCase
        ["fsharP";"nAtiveinterop";"taskbuildereXtensions";"microsoftword"]
        [[0;1];   [0;6];          [0;4;11];               [0;9]]

(* still map2, but extracted arguments *)
let xs7 =
    let args1 = ["fsharP";"nAtiveinterop";"taskbuildereXtensions";"microsoftword"]
    let args2 = [[0;1];   [0;6];          [0;4;11];               [0;9]]
    List.map2 toCamelCase args1 args2

【讨论】:

【参考方案4】:

您可以使用柯里化来应用函数而无需显式声明 lambda:

let inline flip f x y = f y x
...
List.map (flip (||>) toCamelCase)

或更改toCamelCase或输入列表中的参数顺序,这样您就不需要定义flip

List.map ((||>) toCamelCase)

但我认为最好的选择是避免||>

List.map (fun (word, indexes) -> toCamelCase word indexes)

【讨论】:

不需要你的flip,F#默认有currying,翻转参数没有问题。问题是将元组传递给非元组函数。但我同意避免||>

以上是关于`fun` lambda 表达式有速记语法吗?的主要内容,如果未能解决你的问题,请参考以下文章

Lambda 表达式常用语法

Java8新特性Lambda表达式基础语法,都在这儿了!!

lambda表达式

python中的lambda表达式引起的问题? [复制]

你能解释一下 lambda 表达式吗? [复制]

lambda函数