所以基本上如果我在F#中有一个简单的列表
let ls = [3; 9; 2; 15; 3]
我想sortDescending然后拿3.我可以做这个链接:
let orderList ls = List.sortDescending ls
let take3 ls = List.take(3) ls
let main ls = (orderList >> take3) ls
这很好用,在使用F#Interactive调试器时可以满足我的需要。但我试图在所有一个表达式中内联,而不是正确的语法。是否可能,我只是没有得到语法。
let main2 ls = List.sortDescending >> List.take(3) ls
我正在从更多的面向对象心态转向C#我可以做到这一点:
var ls = new List<int>{ 3, 9, 2, 15, 3 };
var subset = ls.OrderByDescending(x => x).Take(3).ToList();
我只是期待你可以像流畅的语法INLINE一样继续链接,但看起来我缺少语法或者没有简单的东西。我最近才学习F#,但到目前为止我都在挖掘它。我只是很难将某些东西链接在一起。
答案 0 :(得分:15)
>>
运算符接受两个函数并生成一个函数,该函数是这两个函数的组合。
|>
运算符接受一个值和一个函数,并将值传递给函数,生成另一个值。
假设我们有
let addone x = x + 1
let double x = x * 2
然后(addone >> double) 100
和100 |> addone |> double
都是202.你明白为什么吗?确保清楚。
像这样想想他们。 f >> g
是组成:
let compose f g =
fun x -> g (f x)
所以
(addone >> double) 100
与
相同(fun x -> double ( addone x ) ) 100
与
相同double (addone 100)
x |> f
是管道:
let pipe x f = f x
所以
100 |> addone |> double
与
相同(addone 100) |> double
与
相同double (addone 100)
在C#中,将扩展方法链接在一起的业务基本上就是管道运算符:
customers.Take(100).ToList()
与
相同Enumerable.ToList(Enumerable.Take(customers, 100))
答案 1 :(得分:2)
我觉得你很困惑,因为你错过了F#中关于括号的一些细节。你写了这个:
let main2 ls = List.sortDescending >> List.take(3) ls
我认为你可能认为List.take
只用一个参数调用,但事实并非如此。请注意,在F#括号中只能组合事物。它们不需要调用函数。在您的情况下,您的括号实际上没有任何效果。看看那两个函数调用。它们具有相同的含义。
List.take (3) (ls)
List.take 3 ls
如果这让你感到困惑,那就想想简单的数学。这里的括号只是组合的东西。这意味着,首先计算括号内的任何内容。
(3 * 6) + 10
3 * 6 + 10
同样在上述情况下,您可以得到相同的结果,有或没有括号。如果需要其他优先级,则只需要括号。就像首先添加6 + 10
然后将结果乘以3一样。
3 * (6 + 10)
在F#中它是。首先计算括号内的内容。在上面的示例中,您只需将3
放入其中。这完全没有意义。这就像你写的那样。
(3) * (6) + (10)
另见一个例子:
sqrt(2.0) + 2.0
sqrt 2.0 + 2.0
这两个例子的意思相同。这可能是你的困惑。但是用括号解释它是如何被解释的正确方法是这样写的:
(sqrt 2.0) + 2.0
围绕(2.0)
的第一个括号没有意义。但是,如果您首先要添加数字,并且想要平方根4.0
,则需要编写:
sqrt (2.0 + 2.0)
回到你的例子。如果我们删除3
周围的括号并将必要的括号添加回代码中,如何解释代码,实际上你写的是这样的:
let main2 ls = List.sortDescending >> List.take(3) ls
let main2 ls = List.sortDescending >> (List.take 3 ls)
这意味着。您的代码首先执行List.take 3 ls
。结果是List
。然后,您尝试使用List.sortDescending
撰写函数List
。这肯定会因类型错误而失败。您可以想要做的只是将List.take
部分应用于一个参数,并使用List.sortDescending
组合生成的函数。在这种情况下,您需要写:
(List.take 3)
或者作为一个完整的例子:
let main2 ls = (List.sortDescending >> (List.take 3)) ls
let main2 ls = (List.sortDescending >> List.take 3) ls
上面两行中的两行再次具有相同的含义。在您的情况下,您现在所做的是创建部分应用函数List.take 3
,然后使用List.sortDescending
进行组合。然后返回一个新函数,然后将ls
作为参数传递。请注意,此代码有点冗长。你也“理论上”可以把它写成:
let main2 ls = (List.sortDescending >> List.take 3) ls
let main2 = List.sortDescending >> List.take 3
请注意,如果不使用具体值调用函数,则可能会产生“值限制”错误。顺便说一句,还有其他方法可以编写你的功能。消除值限制错误的一种方法是:
let main ls = ls |> List.sortDescending |> List.take 3
这与:
相同let main ls = List.take 3 (List.sortDescending ls)
顺便说一下。作为另一个与您的问题没有直接关系的通知。您还应该查看List.truncate
而不是List.take
。如果List.take
无法返回您指定的元素的确切数量,则会引发错误。 List.truncate
永远不会抛出错误。
List.take 4 [1;2;3] // throws an exception
List.truncate 4 [1;2;3] // returns -> [1;2;3]
如果您想了解有关管道,嵌套或组合的更多详细信息。我有一篇博客文章介绍了这些细节:http://sidburn.github.io/blog/2016/09/25/function-application-and-composition
答案 2 :(得分:1)
您当然可以在F#中使用linq扩展方法。
open System
open System.Linq
let ls = [3; 9; 2; 15; 3]
ls.OrderByDescending(fun x -> x).Take(3).ToList()
此外,如果您喜欢这种语法,Fsharp.Core.Fluent可以这样做:
#r @"..\packages\FSharp.Core.Fluent-4.0.1.0.0.5\lib\portable-net45+netcore45+wpa81+wp8+MonoAndroid1+MonoTouch1\FSharp.Core.Fluent-4.0.dll"
#r "System.Runtime"
open FSharp.Core.Fluent
open System.Runtime
ls.sortDescending().Take(3).toList()
请注意.ToList()
之间的差异,这是.NET的通用列表:
Collections.Generic.List<int> = seq [15; 9; 3]
和.toList()
,这是F#的链接列表:val it : int list = [15; 9; 3]
。