5

有一个常见问题是 F#本身不支持中缀样式使用 Haskell 中可用的函数:

isInfixOf :: Eq a => [a] -> [a] -> Bool
isInfixOf "bar" "foobarbaz"
"bar" `isInfixOf` "foobarbaz"

可以在这里找到最知名的 F# 解决方案:

let isInfixOf (what:string) (where:string) =
    where.IndexOf(what, StringComparison.OrdinalIgnoreCase) >= 0
let found = "bar" |>isInfixOf<| "foobarbaz"

此外,使用本地运算符优先级很容易对其进行一些改进:

let ($) = (|>)
let (&) = (<|)
let found = "bar" $isInfixOf& "foobarbaz"

还有 XML-ish </style/>在此处进行了描述。

我想找到一个更好的解决方案,具有以下标准:

  • 不破坏常用运算符的单字符运算符(或一对);
  • 它应该是相同的字符,同样重音(反引号)字符在 Haskell 中服务;
  • 它不应该破坏关联性(支持链接):

    let found = "barZZZ" |>truncateAt<| 3 |>isInfixOf<| "foobarbaz"
    
  • 可选地,它应该支持采用元组的函数:

    let isInfixOf (what:string, where:string) = ...
    // it will not work with |> and <|
    
  • 或者,它应该优雅地处理函数/3:

    val f: 'a -> 'b -> 'c -> 'd = ...
    let curried = a |>f<| b c
    // this wouldn't compile as the compiler would attempt to apply b(c) first
    

PS 也欢迎各种编码技巧,因为我相信好的技巧(当 F# 开发团队检查时)可以成为未来语言的一部分。

4

2 回答 2

17

我同意在某些情况下,在 Haskell 中将函数转换为中缀运算符的能力很巧妙。但是,我不确定此功能是否适合通常的 F# 编程风格,因为可以使用成员来实现相同的功能。

例如,让我们使用您的代码片段truncateAtand isInfixOf

let found = "barZZZ" |>truncateAt<| 3 |>isInfixOf<| "foobarbaz" 

如果我们将TruncateAtand定义IsInfixOf为 的扩展方法string,那么您可以编写:

let found = "barrZZZ".TruncateAt(3).IsInfixOf("foobarbaz") 

这个版本更短,我个人认为它也更具可读性(尤其是对于具有 .NET 编程背景而不是 Haskell 背景的人)。您还可以在点击 时获得 IntelliSense .,这是一个不错的奖励。当然,您必须将这些操作定义为扩展方法,因此您需要更仔细地考虑库的设计。

为了完整起见,扩展方法定义如下:

type System.String with
  member what.IsInfixOf(where:string) = 
    where.IndexOf(what, StringComparison.OrdinalIgnoreCase) >= 0 
  member x.TruncateAt(n) = 
    x.Substring(0, n)
于 2012-09-19T16:37:21.183 回答
2
let (|<) f x = f x
let f = sprintf "%s%s"
let x = "A" |> f |< "B" |> f |< "C" //"ABC"
于 2012-09-19T16:23:12.917 回答