每周源代码34- F#的兴起
[原文发表地址] The Weekly Source Code 34 - The Rise of F#
[原文发表时间] 2008-09-25 08:49 AM
首先,我要提醒大家,请大家看看我正在进行的最新的要求阅读源代码以编出更好的程序。亲爱的读者,我为大家呈上" 每周源代码."的第34篇,之后还将继续。
F#,大家都急于了解F#和它的功能,F#2008年九月的CTP是在几周前出来的。和我讨论的同事也正在努力发掘它。下面是我们的目标:
“为了研究编程语言而开发的F#将炙手可热的类型安全、简洁、性能、表达性脚本和高质量,获得良好支持的现代运行库系统相结合。”
它看起来更像一种研究语言,因为它内置于Visual Studio中,而且在语法突出显示、智能感知等方面都看起来很不错。亲爱的读者,这些信息对你来说可能是昨日黄花了,但是F#已经优化到更高的水平。这款发布将F#推向了和C# ,VB同等位置。它还有了自己的在MSDN上的F# 研发中心。似乎还蛮有模有样的。
Vertigo's Rick Taylor在下面的图示中解释了F#的内容,意义以及方法:
“除非你特别制定,否则F#是不可改变的,在一定程度上有点像C#的构造字符串。它会拓展到一些你未曾预期的地方。例如,一旦你在一些特定值中设置了int,你就不能改变了,除非你将其标记为可以改变。出现这一情况的原因是语言是主要的功能之一。函数式语言的编程中包含函数的返回值-但是每个值或每个集合的值都是自己的实体,如果在一个方法中改变它,将产生副作用 ,即在功能语言中让人不快的因素严格意义上,函数返回值,但不会改变参数或外面的部分(这就附带的产生了前面提到的单子模式)。F#虽遵循函数编程的规则,但也允许使用可变关键字来打破这一规则。
学习F#的最佳途径是什么呢?这里倒有几个不错的办法。首先,有点高调的自我推销下吧,我和几个优秀的F#的开发人士合做了两个关于F#的博客,还做了几个不错的关于.NET Rocks和Herding Code episodes的博客。
Starting Small with F# with Dustin Campbell
.NET Rocks had Ted Neward and Amanda Laucher talking F# recently
F# on Herding Code - Matt Podwysocki digging into F#
图书
- 我很喜欢Robert的F# 基础这本书
- 另外,如果你是个狂热的忍者火箭科学家,你也可以阅读"为科学家开发的F# ."。说真的,我个人非常建议你到Borders购买这本书。狂热的科学家。(说真的,这真书超级棒,你也可以看到一些免费的章节)
- Don Syme是开发F#的鼻祖,他的这本"F# 专家"在市面上也有卖,而且销量极高。
F# 博客
- Don Syme的博客
- Luke Hoban的博客
- Jomo Fisher的博客 和他的10秒后从0开始执行的文章.
- Brian McNamara的博客
- Andrew Kennedy的博客
- Dustin Campbell的博客可爱的 F# 实例分类可能有些乏味,F#-esque所有东西最终从hubFS获取。请查看他的F# 交互博客 and Project Euler 分类。
- Chris Smith的 20分钟看F#
- Matthew Podwysocki的 F# 分类。
代码
为C# 爱好者做的F# 介绍
然而,作为F#新秀,我有多年的Haskell经验,让我博得满堂彩的是Leon的 为C# 爱好者做的F#介绍演示文稿的展示。我知道,这听起来令人难以置信。因为你可能了解到,Leon就是那个最近在澳大利亚TechEd的公众场合称我为Hanselgirl (我的名字其实是Hanselman ) 的嚣张的家伙。我当即给了他一记耳光。我们还公开比腕力。我们两个之间肯定有互相合作的潜力,但是我跑题了。你可以点击在这里下载下载他的deck ,我也很不客气地把它传到了SlideShare上,并为C# 爱好者做的F# 介绍
查看SlideShare网站的显示或上传。
我把第45至45张幻灯片智能化了,但是他描述的方法是点击。他做的也正是我想做的:
“我是靠Reflecto完成的,不是靠看书,论文或听播客或拷问高手。一个简单的let语句实质上就是一个静态函数。当你看到let, 你就应该想到函数。
看看那些幻灯片吧。这是个不错的平台,我保证Leon不会介意的。亲爱的读者,在你的本地用户组显示他的谈话吧,或者在工作之余享受一次程序员午餐,并把功劳归功于它。这也是我为什么计划这么周全的原因。Suck it Bambrick!*
让我们看看三个代码位置。
首先,基本的。
记住,没有变量,也无副作用。
1: #light
2:
3: let sqr x = x * x
4: let multiply x y = x * y
5:
6: print_int (sqr 3)
7: print_int (multiply 3 4)
8:
9: // recursive function fibonacci series using pattern matching
10: let rec fib x =
11: match x with
12: | x when x <= 0 -> failwith "An integer greater than 0 is required."
13: | 1 -> 1
14: | 2 -> 1
15: | x -> fib (x - 1) + fib (x - 2)
16:
17: print_int (fib 15)
18:
19: // functions as values
20: let add x y = x + y
21: let a1 = add 3
22: let a2 = a1 4
23:
24: print_int a2
其次,较出彩的。
F#中的度量单位。什么?太巧了吧,Andrew Kennedy13年前的博士论文写的就是关于F#的性能。他的论文分为三个部分:
基本上他们为度量单位添加了静态检查和推论。并没有介绍具体的东西,只是介绍了度量单位概念本身。
“就F# 而论, ft和m之间是没有任何关系的。由程序员自主定义合适的换算系数。”
这张图从他博客上截下来的,有近千个单词,展示了FSharp,Math,PhysicalConstants namespace和国际单位制(SI)命名空间。
同样,下面是从Andrew博客上截下来的:
“你可以用 ”and” 互相定义递归测度把它们连接起来,并把度量属性置于度量名称之前。“
1: type [<Measure>] km =
2: static member toM = 1.0/1000.0<m/km>
3: and [<Measure>] m =
4: static member toKm = 1000.0<km/m>
第三,较愚蠢的
Phil Trelford从去年开始,仅仅用182行F#编写了有趣的2D Tron-Clone游戏。你可以从hubFS下载它,参见他怎样在Applied Games Group Blog上写出它。该游戏还支持Xbox 360 controllers哦!真是太棒了。
下面是所有的182行代码,前四行的评论不包括在内。
1: //-----------------------------------------------------------------------------
2: // LightCycles.fs Mini game using windows forms
3: // 2007 written by Phillip Trelford
4: //-----------------------------------------------------------------------------
5:
6: #light
7:
8: #if DIRECTX
9: #R @"C:\WINDOWS\assembly\GAC_32\Microsoft.DirectX\2.0.0.0__31bf3856ad364e35\Microsoft.DirectX.dll" // Feb 206
10: open Microsoft.DirectX.XInput // Required to read XBox 360 controllers
11: #endif
12:
13: open System
14: open System.Drawing
15: open System.Windows.Forms
16:
17: /// Game states
18: type GameState = | Start | Play | Over
19:
20: /// Form key handler type
21: type KeyHandler (form:Form) =
22: do form.KeyPreview <- true
23: let keys = Enum.GetValues (type Keys) :?> (Keys [])
24: let keysDown = Array.create keys.Length false
25: let FindKeyIndex code = keys |> Array.find_index (fun x -> code = x)
26: do form.KeyDown.Add (fun e -> keysDown.[FindKeyIndex e.KeyCode] <- true)
27: do form.KeyUp.Add (fun e -> keysDown.[FindKeyIndex e.KeyCode] <- false)
28: member this.IsKeyDown (keyCode:Keys) = keysDown.[FindKeyIndex keyCode]
29: member this.AnyKeyDown () = keysDown |> Array.exists (fun x -> x)
30:
31: /// Player direction type
32: type Direction = | Left | Right | Up | Down
33:
34: /// Player type
35: type Player (color,startX,startY,direction,keys,keyHandler:KeyHandler) =
36: let mutable x = startX
37: let mutable y = startY
38: let mutable d = direction
39:
40: member this.Color = color
41: member this.X = x
42: member this.Y = y
43: member this.Keys = keys
44:
45: /// Reset player to start values
46: member this.Reset () = x <- startX; y <- startY; d <- direction
47:
48: /// Updates player position
49: member this.Update i =
50: // Read keyborad
51: let mutable newD = d
52: let up, down, left, right = keys
53: if keyHandler.IsKeyDown(up) then newD <- Up
54: if keyHandler.IsKeyDown(down) then newD <- Down
55: if keyHandler.IsKeyDown(left) then newD <- Left
56: if keyHandler.IsKeyDown(right) then newD <- Right
57: #if DIRECTX
58: // Read XBox 360 controller
59: let state = Controller.GetState(i)
60: if state.IsConnected then
61: let pad = state.GamePad
62: if pad.UpButton then newD <- Up
63: if pad.DownButton then newD <- Down
64: if pad.LeftButton then newD <- Left
65: if pad.RightButton then newD <- Right
66: #endif
67: /// Don't allow suicide move
68: match (d,newD) with
69: | (Left, Right) | (Right, Left) | (Up, Down) | (Down, Up) -> ()
70: | _ -> d <- newD
71: /// Update position with direction
72: match d with
73: | Up -> y <- y - 1
74: | Down -> y <- y + 1
75: | Left -> x <- x - 1
76: | Right -> x <- x + 1
77:
78: /// Main form
79: let form = new Form (Text="Light Cycles", Width=680, Height=544)
80:
81: do /// Layout for game window and status panel
82: let layout = new TableLayoutPanel(Dock=DockStyle.Fill, ColumnCount = 2)
83: layout.ColumnStyles.Add( ColumnStyle(SizeType = SizeType.Percent, Width = 100.0f ) ) |> ignore
84: layout.ColumnStyles.Add( ColumnStyle(SizeType = SizeType.Absolute, Width = 128.0f) ) |> ignore
85: /// Play area in pixels
86: let playArea = 500
87: /// Game play area bitmap
88: let bm = new Bitmap(playArea, playArea)
89: /// Clears screen
90: let ClearScreen () =
91: using (Graphics.FromImage(bm)) (fun graphics -> graphics.Clear(Color.Black))
92: /// Draws text to screen
93: let DrawText s =
94: using (Graphics.FromImage(bm)) (fun graphics ->
95: let rect = new RectangleF(0.0f,0.0f,float32 playArea,float32 playArea)
96: let align = new StringFormat(Alignment=StringAlignment.Center, LineAlignment=StringAlignment.Center)
97: graphics.DrawString(s, form.Font, Brushes.White, rect, align)
98: )
99: // Initialise screen
100: ClearScreen ()
101: DrawText "Press any key to start"
102: /// PictureBox to contain game bitmap
103: let pictureBox = new PictureBox(Dock=DockStyle.Fill)
104: pictureBox.Image <- bm
105: layout.Controls.Add(pictureBox)
106:
107: let keyHandler = KeyHandler (form)
108:
109: /// Players array
110: let players =
111: [| Player (Color.Red,playArea/2+20,playArea/2,Down,(Keys.Q,Keys.A,Keys.Z,Keys.X),keyHandler);
112: Player (Color.LightBlue,playArea/2-20,playArea/2,Up,(Keys.P,Keys.L,Keys.N,Keys.M),keyHandler) |]
113: players |> Array.iter (fun player -> bm.SetPixel(player.X,player.Y,player.Color))
114:
115: /// Display player controls
116: let statusPanel = new TableLayoutPanel(Dock=DockStyle.Fill, ColumnCount=1, BackColor=Color.DarkGray)
117: players |> Array.iteri (fun i player ->
118: let name =
119: [| ((new Label (Text=sprintf "Player %d" i, ForeColor=player.Color)) :> Control) |]
120: let up, down, left, right = player.Keys
121: let controls =
122: Array.combine [|"Up";"Down";"Left";"Right"|] [|up;down;left;right|]
123: |> Array.map (fun (name,key) -> (new Label (Text=sprintf "%s '%O'" name key)) :> Control )
124: Array.append name controls
125: |> statusPanel.Controls.AddRange
126: )
127: layout.Controls.Add(statusPanel)
128: form.Controls.Add(layout)
129:
130: /// Game play - returns true if there has been a collision otherwise false
131: let PlayGame () =
132: let collisions = players |> Array.mapi (fun i player ->
133: player.Update i
134: let x, y = (player.X, player.Y)
135: let wall = x < 0 || x >= playArea || y < 0 || y >= playArea
136: if wall then
137: true
138: else
139: let bgColor = bm.GetPixel(x, y)
140: bm.SetPixel (x, y, player.Color)
141: players |> Array.exists (fun player -> let c = player.Color in c.R = bgColor.R && c.G = bgColor.G && c.B = bgColor.B )
142: )
143: pictureBox.Refresh ()
144:
145: match collisions |> Array.tryfind_index (fun x -> x = true) with
146: | Some(i) -> i
147: | None -> (-1)
148:
149: /// Current game state
150: let gameState = ref GameState.Start
151: let gameOverWaitCount = ref 200
152: let r = new Random()
153:
154: /// Timer instance
155: let timer = new Timer()
156: timer.Interval <- 1000/50
157: // Timer event
158: timer.Tick.Add (fun _ ->
159: match !gameState with
160: | Start ->
161: if keyHandler.AnyKeyDown () then
162: ClearScreen ()
163: gameState := GameState.Play
164:
165: | Play ->
166: let i = PlayGame ()
167: if i>=0 then
168: gameState := GameState.Over
169: gameOverWaitCount := 200
170: DrawText (sprintf "Game Over - Play %d Lost" i)
171: pictureBox.Refresh ()
172: | Over ->
173: // Shake screen
174: form.Left <- form.Left + if !gameOverWaitCount > 150 then r.Next(5) - 2 else 0
175: // Decrement Game Over wait
176: decr gameOverWaitCount
177: if !gameOverWaitCount <= 0 then
178: gameState := GameState.Start
179: players |> Array.iter (fun player -> player.Reset ())
180: ClearScreen ()
181: DrawText "Press any key to start"
182: pictureBox.Refresh ()
183: )
184: timer.Start ()
185:
186: [<STAThread>]
187: do Application.Run(form)
尝试每一年都学习一样新的语言