题
看起来 StackOveflow 中有一组 F# 爱好者。
我想更好地了解这门语言,所以,除了 函数式编程理论, ,您能给我指出开始使用 F# 语言的更好起点吗?我的意思是,教程、操作方法,但首先是工作示例,以便有机会开始做某事并享受这门语言。
多谢
安德里亚
解决方案
不是为了让自己太糟糕,而是我在博客上写了几篇 F# 概述文章 这里 和 这里. 。Chris Smith(MS 的 F# 团队成员)有一篇名为“20 分钟内实现 F#”的文章 - 第1部分 和 第2部分.
请注意,您必须小心,因为与以前的版本相比,F# 的最新 CTP(版本 1.9.6.0)有一些严重的破坏性更改,因此,如果不进行修改,一些示例/教程可能无法工作。
这是一些很酷的东西的快速概述,也许我可以自己在这里给你一些明显的提示 非常 简短,可能不是很好,但希望能给你一些可以玩的东西!:-
首先注意 - 互联网上的大多数示例都假设“轻量级语法”已打开。要实现此目的,请使用以下代码行:-
#light
这样您就不必插入某些为 OCaml 兼容性而存在的关键字,也不必用分号终止每一行。请注意,使用此语法意味着缩进定义范围。这将在后面的示例中变得清晰,所有这些示例都依赖于打开的轻量级语法。
如果您使用交互模式,则必须用双分号终止所有语句,例如:-
> #light;;
> let f x y = x + y;;
val f : int -> int -> int
> f 1 2;;
val it : int = 3
请注意,交互模式在每行后返回一个“val”结果。这提供了有关我们正在制定的定义的重要信息,例如 'val f :int -> int -> int' 表示接受两个 int 的函数返回一个 int。
请注意,只有在交互中我们才需要用分号来终止行,而在实际定义 F# 代码时我们不需要这样:-)
您可以使用“let”关键字定义函数。这可能是所有 F# 中最重要的关键字,您将经常使用它。例如:-
let sumStuff x y = x + y
let sumStuffTuple (x, y) = x + y
我们可以这样调用这些函数:-
sumStuff 1 2
3
sumStuffTuple (1, 2)
3
请注意,这里有两种不同的定义函数的方法 - 您可以用空格分隔参数或在“元组”中指定参数(即括号内的值以逗号分隔)。不同之处在于,我们可以使用“部分函数应用”来使用第一种方法来获取参数少于所需参数的函数,而第二种方法则不行。例如。:-
let sumStuff1 = sumStuff 1
sumStuff 2
3
请注意,我们从表达式“sumStuff 1”获取一个函数。当我们可以像传递数据一样轻松地传递函数时,这被称为具有“一流函数”的语言,这是任何函数式语言(例如 F#)的基本部分。
模式匹配非常酷,它基本上就像类固醇上的 switch 语句(是的,我从另一个 F#-ist 那里抄袭了这句话:-)。你可以做这样的事情:-
let someThing x =
match x with
| 0 -> "zero"
| 1 -> "one"
| 2 -> "two"
| x when x < 0 -> "negative = " + x.ToString()
| _ when x%2 = 0 -> "greater than two but even"
| _ -> "greater than two but odd"
请注意,当我们想要匹配某些内容但我们返回的表达式不依赖于输入时,我们使用“_”符号。
我们可以根据需要使用 if、elif 和 else 语句来缩写模式匹配:-
let negEvenOdd x = if x < 0 then "neg" elif x % 2 = 0 then "even" else "odd"
F# 列表(在下面以链接列表的形式实现)可以这样操作:-
let l1 = [1;2;3]
l1.[0]
1
let l2 = [1 .. 10]
List.length l2
10
let squares = [for i in 1..10 -> i * i]
squares
[1; 4; 9; 16; 25; 36; 49; 64; 81; 100]
let square x = x * x;;
let squares2 = List.map square [1..10]
squares2
[1; 4; 9; 16; 25; 36; 49; 64; 81; 100]
let evenSquares = List.filter (fun x -> x % 2 = 0) squares
evenSqares
[4; 16; 36; 64; 100]
请注意,List.map 函数将平方函数“映射”到从 1 到 10 的列表,即将函数应用于每个元素。List.filter 通过仅返回列表中传递所提供的谓词函数的值来“过滤”列表。另请注意“fun x -> f”语法 - 这是 F# lambda。
请注意,自始至终我们都没有定义任何类型 - F# 编译器/解释器“推断”类型,即从使用中找出你想要的东西。例如:-
let f x = "hi " + x
这里编译器/解释器将确定 x 是字符串,因为您正在执行需要 x 是字符串的操作。它还确定返回类型也将为字符串。
当存在歧义时,编译器会做出假设,例如:-
let f x y = x + y
这里 x 和 y 可以是多种类型,但编译器默认为 int。如果你想定义类型,你可以使用类型注释:-
let f (x:string) y = x + y
另请注意,我们必须将 x:string 括在括号中,我们经常必须这样做来分隔函数定义的各个部分。
F# 中两个真正有用且频繁使用的运算符分别是管道转发和函数组合运算符 |> 和 >>。
我们这样定义|>:-
let (|>) x f = f x
请注意,您可以在 F# 中定义运算符,这非常酷:-)。
这允许您以更清晰的方式编写内容,例如:-
[1..10] |> List.map (fun x -> x * x) |> List.filter (fun x -> x % 2 = 0)
将使您获得前 10 个偶数方块。这比以下内容更清楚:-
List.filter (fun x -> x % 2 = 0) (List.map (fun x -> x * x) [1..10])
好吧,至少我这么认为:-)
>> 运算符定义的函数组合定义如下:-
let (>>) f g x = g(f(x))
IE。您仅通过管道转发操作,而第一个函数的参数保持未指定状态。这很有用,因为您可以执行以下操作:-
let mapFilter = List.map (fun x -> x * x) >> List.filter (fun x -> x % 2 = 0)
这里mapFilter将接受一个列表输入并返回像以前一样过滤的列表。这是以下内容的缩写版本:-
let mapFilter = l |> List.map (fun x -> x * x) |> List.filter (fun x -> x % 2 = 0)
如果我们想编写递归函数,我们必须通过在 let 之后放置“rec”来将函数定义为递归函数。下面的例子。
一些很酷的东西:-
阶乘
let rec fact x = if x <= 1 then 1 else x * fact (x-1)
第 n 个斐波那契数
let rec fib n = if n <= 1 then n else fib (n-1) + fib (n-2)
菲兹巴兹
let (/%) x y = x % y = 0
let fb = function
| x when x /% 15 -> "FizzBuzz"
| x when x /% 3 -> "Fizz"
| x when x /% 5 -> "Buzz"
| x -> x.ToString()
[1..100] |> List.map (fb >> printfn "%s")
无论如何,这是一个 非常 简单介绍一下,希望对大家有帮助!!
其他提示
毫无疑问,你应该购买 Don Syme 的优秀著作《Expert F#》。这本书写得非常好,适合初学者和专家。在其中,您会发现介绍性材料和更具挑战性的材料。近600页,物有所值。
我发现它教会了我许多有用的技术来编写功能更强大的 C#,并提供了开始编写 Windows 托管的 F# 应用程序所需的所有参考材料。
该书由 Apress 出版,并有一个随附的网站:http://www.expert-fsharp.com/default.aspx
@kronoz - 非常感谢您的长篇回答,这是一个非常好的起点。我会听从你的建议,并寻找 @vecstasy 提到的书。
现在,让我开始编码:-)
let thanksalot = "thanks a lot"
printfn "%s" (thanksalot);;
如果您在 Visual Studio 中拥有当前的 CTP 版本,它可以让您创建一个 F# Tutorial 项目,该项目为您提供一个 Tutorial.fs,其中包含其名称所暗示的内容。
该教程还指出了更大的集合 Microsoft 的 F# 示例.
此外,还有一个 F# 示例项目正在进行中 代码库.
希望这可以帮助,
米歇尔