Share via


每周源代码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

* F# with Robert Pickering

* .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# 博客

代码

为C# 爱好者做的F# 介绍

然而,作为F#新秀,我有多年的Haskell经验,让我博得满堂彩的是Leon的C# 爱好者做的F#介绍演示文稿的展示。我知道,这听起来令人难以置信。因为你可能了解到,Leon就是那个最近在澳大利亚TechEd的公众场合称我为Hanselgirl (我的名字其实是Hanselman 的嚣张的家伙。我当即给了他一记耳光。我们还公开比腕力。我们两个之间肯定有互相合作的潜力,但是我跑题了。你可以点击在这里下载下载他的deck ,我也很不客气地把它传到了SlideShare上,并为C# 爱好者做的F# 介绍

查看SlideShare网站的显示上传

(标签: f# c# )

我把第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)命名空间。

image

同样,下面是从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哦!真是太棒了。

image

下面是所有的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)

尝试每一年都学习一样新的语言