1. Trang chủ
  2. » Công Nghệ Thông Tin

Foundations of F#.Net phần 6 pps

35 280 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 35
Dung lượng 443,08 KB

Nội dung

The untyped_ Functions The .NET Framework’s BCL contains two versions of the IEnumerable interface, one defined in S ystem.Collections.Generic a nd an older one defined in S ystem.Collections . All the samples shown so far have been designed to work with the new generic version from System. Collections.Generic . However, sometimes it might be necessary to work with collections that are not generic, so the F# IEnumerable module also provides a number of functions to work with nongeneric collections. These functions all start with the prefix untyped and then have the same name as their generic counterpart. The big disadvantage of using these functions is that they do not contain any type information; therefore, the compiler cannot type check them properly, which can mean some code might throw an invalid cast exception at runtime. Before using these functions, I strongly recommend that you see whether you can use the list comprehension syntax covered in Chapters 3 and 4 instead. This is because the list com- prehension syntax can infer the types of many untyped collections, usually by looking at the type of the Item indexer property, so there is less need for type annotations, which generally makes programming easier. The following example looks at an array list that stores a sequence of integers and then uses untyped_map to double each of the integers in the list: #light open System.Collections let arrayList = let temp = new ArrayList() temp.AddRange([| 1; 2; 3 |]) temp let doubledArrayList = arrayList |> Seq.untyped_map (fun x -> x * 2) doubledArrayList |> Seq.untyped_iter (fun x -> printf "%i " x) The results of this code, when compiled and executed, are as follows: 2 4 6 As y ou can see fr om the pr evious example, when the programmer gets the types right, using the untyped functions is pretty straightforward. However, consider the following exam- ple that tries to perform the same operation on the list, except this time it contains strings: #light open System.Collections let stringArrayList = let temp = new ArrayList() temp.AddRange([| "one"; "two"; "three" |]) temp CHAPTER 7 ■ THE F# LIBRARIES 148 7575Ch07.qxp 4/27/07 1:03 PM Page 148 let invalidCollection = stringArrayList |> Seq.untyped_map (fun x -> x * 2) invalidCollection |> Seq.untyped_iter (fun x -> printf "%O " x) The results of this code, when compiled and executed, are as follows: System.InvalidCastException: Specified cast is not valid. at Microsoft.FSharp.Collections.IEnumerator.untyped_map@25.get_Current() at Microsoft.FSharp.Collections.IEnumerator.untyped_iter@12[A,U](FastFunc`2 f, U e) at <StartupCode>.FSI_0011._main() stopped due to error It’s easy to see that using untyped collections places an extra burden on the programmer to ensure the types in the collection are correct. So, I highly recommend that if for some rea- son you must use an untyped collection, then it is best to convert it to a typed collection. This is done using the function untyped_to_typed, which is demonstrated in the following example: #light open System.Collections open System.Collections.Generic let floatArrayList = let temp = new ArrayList() temp.AddRange([| 1.0; 2.0; 3.0 |]) temp let (typedIntList : seq<float>) = Seq.untyped_to_typed floatArrayList Using untyped_to_typed always required using type annotations to tell the compiler what type of list you are producing. Here you have a list of floats, so you use the type annotation IEnumerable<float> to tell the compiler it will be an IEnumerable collection containing floating-point numbers. The Microsoft.FSharp.Core.Enum Module The Enum module is a simple module to help deal with enumerations in F#. I will cover the fol- lowing functions: to_int and of_int: F unctions for converting to and from integers combine and test: Functions for combining enums with a bitwise “and” and testing whether a bit is set CHAPTER 7 ■ THE F# LIBRARIES 149 7575Ch07.qxp 4/27/07 1:03 PM Page 149 The to_int and of_int Functions The following example shows how to convert from an enumeration to an integer and then c onvert it back to an enumeration. Converting from an enumeration to an integer is straight- forward; you just use the to_int function. Converting back is slightly more complicated; you use the of_int function, but you must provide a type annotation so that the compile knows which type of enumeration to convert it to. You can see this in the following sample where you add the annotation DayOfWeek to the identifier dayEnum: #light open System let dayInt = Enum.to_int DateTime.Now.DayOfWeek let (dayEnum : DayOfWeek) = Enum.of_int dayInt print_int dayInt print_newline () print_any dayEnum The results of this code, when compiled and executed, are as follows: 0 Sunday The combine and test Functions The other common tasks that you need to perform with enumerations is to combine them using a logical “or” and then test them using a logical “and.” The functions combine and test are provided to fulfill these roles. The function combine takes a list of enumerations and com- bines them into one enumeration. The function test tests whether a particular enumeration is part of a combined enumeration. The following example combines two enumeration values, AnchorStyles.Left and AnchorStyles.Left, and then uses test to test the resulting enumeration: #light open System.Windows.Forms let anchor = Enum.combine [AnchorStyles.Left ; AnchorStyles.Left] printfn "(Enum.test anchor AnchorStyles.Left): %b" (Enum.test anchor AnchorStyles.Left) printfn "(Enum.test anchor AnchorStyles.Right): %b" (Enum.test anchor AnchorStyles.Right) The results of this code, when compiled and executed, are as follows: (Enum.test anchor AnchorStyles.Left): true (Enum.test anchor AnchorStyles.Right): false CHAPTER 7 ■ THE F# LIBRARIES 150 7575Ch07.qxp 4/27/07 1:03 PM Page 150 Enum types marked with the System.Flags attribute also support the use of the &&& and ||| operators to perform these operations directly. For example, you could write the previous code as follows: #light o pen System.Windows.Forms let anchor = AnchorStyles.Left ||| AnchorStyles.Left printfn "test AnchorStyles.Left: %b" (anchor &&& AnchorStyles.Left <> Enum.of_int 0) printfn "test AnchorStyles.Right: %b" (anchor &&& AnchorStyles.Right <> Enum.of_int 0) The Microsoft.FSharp.Text.Printf Module The Printf module provides functions for formatting strings in a type-safe way. The functions in the Printf module take a string with placeholders for values as their first argument. This returns another function that expects values for the placeholders. You form placeholders by using a percentage sign and a letter representing the type that they expect. Table 7-1 shows the full list. Table 7-1. Printf Placeholders and Flags Flag Description %b bool, formatted as “true” or “false.” %s string, formatted as its unescaped contents. %d, %i Any basic integer type (that is, sbyte, byte, int16, uint16, int32, uint32, int64, uint64, nativeint, or unativeint) formatted as a decimal integer, signed if the basic integer type is signed. %u Any basic integer type formatted as an unsigned decimal integer. %x, %X, %o Any basic integer type formatted as an unsigned hexadecimal, (a-f)/Hexadecimal (A-F)/Octal integer. %e, %E Any basic floating-point type (that is, float or float32), formatted using a C-style floating-point format specification, signed value having the form [-]d.dddde[sign]ddd where d is a single decimal digit, dddd is one or more decimal digits, ddd is exactly three decimal digits, and sign is + or –. %f Any basic floating-point type, formatted using a C-style floating-point format specification, signed v alue having the for m [-]dddd.dddd, wher e dddd is one or more decimal digits. The number of digits before the decimal point depends on the magnitude of the number , and the number of digits after the decimal point depends on the requested precision. %g, %G Any basic floating-point type, formatted using a C-style floating-point format specification, signed value printed in f or e format, whichever is more compact for the giv en v alue and pr ecision. %M System.Decimal v alue . %O Any value, printed by boxing the object and using its ToString method(s). %A Any v alue , pr inted using the any_to_string method that pr etty pr ints F# types . continued CHAPTER 7 ■ THE F# LIBRARIES 151 7575Ch07.qxp 4/27/07 1:03 PM Page 151 Table 7-1. Continued Flag Description % a A general format specifier; requires two arguments: A function that accepts two arguments: a context parameter of the appropriate type for the given formatting function (such as a System.IO.TextWriter) and a value to print and that either outputs or returns appropriate text. The particular value to print. %t A general format specifier; requires one argument: a function that accepts a context parameter of the appropriate type for the given formatting function (such as a System.IO.TextWriter) and that either outputs or returns appropriate text. 0 A flag that adds zeros instead of spaces to make up the required width. - A flag that left justifies the result within the width specified. + A flag that adds a + character if the number is positive (to match the – sign for negatives). ‘ ’ Adds an extra space if the number is positive (to match the – sign for negatives). The following example shows how to use the printf function. It creates a function that expects a string and then passes a string to this function. #light Printf.printf "Hello %s" "Robert" The results of this code are as follows: Hello Robert The significance of this might not be entirely obvious, but the following example will probably help explain it; if a parameter of the wrong type is passed to the printf function, then it will not compile: #light Printf.printf "Hello %s" 1 The previous code will not compile, giving the following error: Prog.fs(4,25): error: FS0001: This expression has type int but is here used with type string This also has an effect on type inference. If you create a function that uses printf, then any arguments that are passed to printf will have their types inferred from this. For example, the function myPrintInt, shown here, has the type int -> unit because of the printf function contained within it: #light let myPrintInt x = Printf.printf "An integer: %i" x CHAPTER 7 ■ THE F# LIBRARIES 152 7575Ch07.qxp 4/27/07 1:03 PM Page 152 The basic placeholders in a Printf module function are %b for a Boolean; %s for a string; %d or %i for an integer; %u for an unsigned integer; and %x, %X, or %o for an integer formatted as a hexadecimal. It is also possible to specify the number of decimal places that are displayed in numeric types. The following example demonstrates this: # light let pi = System.Math.PI Printf.printf "%f\r\n" pi Printf.printf "%1.1f\r\n" pi Printf.printf "%2.2f\r\n" pi Printf.printf "%2.8f\r\n" pi The results of this code are as follows: 3.141593 3.1 3.14 3.14159265 The Printf module also contains a number of other functions that allow a string to be formatted in the same ways as printf itself but allow the result to be written to a different destination. The following example shows some of the different versions available: #light // write to a string let s = Printf.sprintf "Hello %s\r\n" "string" print_string s // prints the string to the given channel Printf.fprintf stdout "Hello %s\r\n" "channel" // prints the string to a .NET TextWriter Printf.twprintf System.Console.Out "Hello %s\r\n" "TextWriter" // create a string that will be placed // in an exception message Printf.failwithf "Hello %s" "exception" The r esults of this code are as follows: Hello string Hello channel Hello TextWriter Microsoft.FSharp.FailureException: Hello exception at Microsoft.FSharp.Text.Printf.failwithf@60.Invoke(String s) at Microsoft.FSharp.Text.PrintfImpl.Make@188.Invoke(A inp)) at <StartupCode>.FSI_0003._main() stopped due to error CHAPTER 7 ■ THE F# LIBRARIES 153 7575Ch07.qxp 4/27/07 1:03 PM Page 153 The Microsoft.FSharp.Control.IEvent Module You can think of an event in F# as a collection of functions that can be triggered by a call to a function. The idea is that functions will register themselves with the event, the collection of f unctions, to await notification that the event has happened. The trigger function is then used to give notice that the event has happened, causing all the functions that have added them- selves to the event to be executed. I will cover the following features of the IEvent module: Creating and handling events: The basics of creating and handling events using the create and add functions The filter function: A function to filter the data coming into events The partition function: A function that splits the data coming into events into two The map function: A function that maps the data before it reaches the event handler Creating and Handling Events The first example looks at a simple event being created using the IEvent module’s create func- tion. This function returns a tuple containing the trigger function as its first item and the event itself as its second item. Often you need to add type annotations to say what type of event you want, that is, what type of parameter your event’s handler functions should take. After this, you use the event’s Add function to add a handler method, and finally you trigger the event using the trigger function: #light let trigger, event = IEvent.create<string>() event.Add(fun x -> printfn "%s" x) trigger "hello" The result of this code is as follows: hello In addition to this basic event functionality, the F# IEvent module provides a number of functions that allow you to filter and partition events to give fine-grained control over which data is passed to which event handler. The filter Function The following example demonstrates how you can use the IEvent module’s filter function so that data being passed to the event is filtered before it reaches the event handlers. In this example , y ou filter the data so that only str ings beginning with H ar e sent to the ev ent handler : CHAPTER 7 ■ THE F# LIBRARIES 154 7575Ch07.qxp 4/27/07 1:03 PM Page 154 #light let trigger, event = IEvent.create<string>() let newEvent = event |> IEvent.filter (fun x -> x.StartsWith("H")) newEvent.Add(fun x -> printfn "new event: %s" x) trigger "Harry" trigger "Jane" trigger "Hillary" trigger "John" trigger "Henry" The results of this code, when compiled and executed, are as follows: new event: Harry new event: Hillary new event: Henry The partition Function The IEvent module’s partition function is similar to the filter function except two events are returned, one where data caused the partition function to return false and one where data caused the partition function to return true. The following example demonstrates this: #light let trigger, event = IEvent.create<string>() let hData, nonHData = event |> IEvent.partition (fun x -> x.StartsWith("H")) hData.Add(fun x -> printfn "H data: %s" x) nonHData.Add(fun x -> printfn "None H data: %s" x) trigger "Harry" trigger "Jane" trigger "Hillary" trigger "John" trigger "Henry" The results of this code are as follows: H data: Harry None H data: Jane H data: Hillary None H data: John H data: Henry CHAPTER 7 ■ THE F# LIBRARIES 155 7575Ch07.qxp 4/27/07 1:03 PM Page 155 The map Function It is also possible to transform the data before it reaches the event handlers. You do this using t he m ap f unction provided in the I Event m odule. The following example demonstrates how to use it: #light let trigger, event = IEvent.create<string>() let newEvent = event |> IEvent.map (fun x -> "Mapped data: " + x) newEvent.Add(fun x -> print_endline x) trigger "Harry" trigger "Sally" The results of this code are as follows: Mapped data: Harry Mapped data: Sally This section has just provided a brief overview of events in F#. You will return to them in more detail in Chapter 8 when I discuss user interface programming, because that is where they are most useful. The Microsoft.FSharp.Math Namespace The Microsoft.FSharp.Math namespace is designed to enable F# to ensure that the F# libraries include definitions of some of the foundational constructs used across a wide range of graphics, mathematical, scientific, and engineering applications. First you will look briefly at the modules that make it up, and then you’ll dive into a more detailed example. It contains arbitrary precision numbers; these are numbers whose values have no upper limit and include the modules BigInt and BigNum. A typical use of these would be in a program that searches for large prime numbers, perhaps for use in cryptography. The modules Matrix, Vector, RowVector, and Notations all contain operations related to matr ices and vectors. M atrices ar e sets of numbers arranged in rows and columns to form a r ectangular arr ay. V ectors are a column of numbers and are like a matrix with one column but are a separate type. A vector is a quantity characterized by magnitude and direction, so a two- dimensional vector is specified by two coordinates, a three-dimensional vector by three coordinates, and so on; therefore, vectors are represented as a matrix made up of one column with the number of rows depending on the dimension of the vector. There is a module, Complex, for working with complex numbers. The complex numbers are the base for many types of fractal images, so I will demonstrate how you can use the F# complex number library to draw the most famous fractal of all, the Mandelbrot set. The Mandelbrot set is generated by repeated iteration of the following equation: C n+1 = C n 2 + c CHAPTER 7 ■ THE F# LIBRARIES 156 7575Ch07.qxp 4/27/07 1:03 PM Page 156 The next number in the series is formed from the current number squared plus the origi- n al number. If repeated iteration of this equation stays between the complex number C(1, 1i) and C(–1, –1i), then the original complex number is a member of the Mandelbrot set. This can be implemented in F# with the following: #light open Microsoft.FSharp.Math open Microsoft.FSharp.Math.Notation let cMax = complex 1.0 1.0 let cMin = complex -1.0 -1.0 let iterations = 18 let isInMandelbrotSet c0 = let rec check n c = (n = iterations) or (cMin < c) && (c < cMax) && check (n + 1) ((c * c) + c0) check 0 c0 The function isInMandelbrotSet tests whether a complex number is in the Mandelbrot set by recursively calling the check function with the new c value of ((c * c) + c0) until either the complex number passes one of the constants cMax or cMin or the number of iterations exceeds the constant iterations. If the number of iterations specified by iterations is reached, then number is a member of the set; otherwise, it is not. Because the complex numbers consist of two numbers, they can be represented in a two- dimensional plane. The Mandelbrot complex numbers exist between C(1, 1i) and C(–1, –1i) so the plane that you need to draw has the origin, which is the point 0, 0, in the center, and its axis extends out in either direction until reaching a maximum of 1.0 and a minimum of –1.0, such as the plane on the right of Figure 7-1. However, when it comes to pixels on a computer screen, you must deal with a plane where the origin is in the top-right corner and it extends rightward and downward. Because this type plane is made up of pixels, which are discrete val- ues, it is represented by integers typically somewhere in the range 0 to 1600. Such a plane appears on the left of Figure 7-1. Figure 7-1. A bitmap plane vs. a complex plane CHAPTER 7 ■ THE F# LIBRARIES 157 7575Ch07.qxp 4/27/07 1:03 PM Page 157 [...]... Functions The Pervasives module also offers a number of functions (see Table 7-2) specifically for floatingpoint numbers, some of which are used in the following sample: 161 7575Ch07.qxp 162 4/27/07 1:03 PM Page 162 CHAPTER 7 I THE F# LIBRARIES #light printfn "(sqrt 16. 0): %f" (sqrt 16. 0) printfn "(log 160 .0): %f" (log 160 .0) printfn "(cos 1 .6) : %f" (cos 1 .6) The results of this code, when compiled and... the outline of an ellipse represented by a rectangle or rectangular set of points DrawPie Draws a portion of the outline of an ellipse, represented by a rectangle and two radial lines representing the start and finish angles DrawLine Draws a single line from two points DrawLines Draws a set of lines from an array of points DrawPolygon Draws the outline of a polygon, which is a closed set of lines from... from an array of points DrawRectangle Draws the outline of a rectangle represented by a coordinate and its width and height DrawRectangles Draws the outline of a set of rectangles from an array of rectangles FillClosedCurve Draws a solid closed curve defined by an array of points FillEllipse Draws a solid ellipse represented by a rectangle or rectangular set of points FillPie Draws a portion of a solid... so that you can tell whether a pixel is part of the complex plane It is easy to perform this mapping in just a few lines of F# code: #light open Microsoft.FSharp.Math open Microsoft.FSharp.Math.Notation let scalingFactor = 1.0 / 200.0 let offset = -1.0 let mapPlane (x, y) = let fx = ((float x) * scalingFactor) + offset let fy = ((float y) * scalingFactor) + offset complex fx fy Once this is complete,... and executed, are as follows: (sqrt 16. 0): 4.000000 (log 160 .0): 5.075174 (cos 1 .6) : -0.029200 Table 7-2 Arithmetic Functions for Floating-Point Numbers Function Description abs_float Returns the absolute value of the argument acos Returns the inverse cosine (arccosine) of the argument, which should be specified in radians asin Returns the inverse sine (arcsine) of the argument, which should be specified... use of these functions is demonstrated here: #light let i = ref 0 (incr i) print_int !i print_newline () (decr i) print_int !i The results of this code are as follows: 1 0 Streams Finally, you’ve come to the last major set of functions within the Pervasives module—functions that read from and write to streams Streams are a way of managing I/O that allows a file, a network connection, or an area of memory... demo" exit 1 printfn "myFlag: %b" !myFlag printfn "myString: %s" !myString printfn "myInt: %i" !myInt printfn "myFloat: %f" !myFloat printfn "myStringList: " print_any !myStringList 165 7575Ch07.qxp 166 4/27/07 1:03 PM Page 166 CHAPTER 7 I THE F# LIBRARIES When run with no command-line arguments or faulty command-line arguments, the program will output this: Arg module demo -set: Sets the value my_flag... Chapter 10 7575Ch08.qxp 4/27/07 1:04 PM CHAPTER Page 167 8 III User Interfaces I n this chapter, you will look at one of the most common tasks a programmer needs to perform—the art of putting pixels on the screen In F# this is all about the libraries and API that you call, and you have a lot of choices in this area You can create WinForms, a set of classes found in System.Windows.Form.dll These classes... (fun e -> if temp.Width - 64 > 0 && temp.Height - 96 > 0 then e.Graphics.FillPie (brush, 32, 32, 7575Ch08.qxp 4/27/07 1:04 PM Page 169 CHAPTER 8 I USER INTERFACES temp.Width - 64 , temp.Height - 64 , 0, 290)) temp Application.Run(form) Figure 8-1 shows the resulting form Figure 8-1 A WinForm containing a pie shape Because this image is linked to the size of the form, you must tell the form to redraw itself... functional programming In some ways this tree-drawing function is satisfactory; it gives a nice hierarchical overview of the tree in a fairly concise 86 lines of F# code However, there is a limit to how well this approach scales As you draw more complicated images, the number of lines of code can grow rapidly, and working out all the geometry can become time-consuming To help manage this complexity, . 1:03 PM Page 161 #light printfn "(sqrt 16. 0): %f" (sqrt 16. 0) printfn "(log 160 .0): %f" (log 160 .0) printfn "(cos 1 .6) : %f" (cos 1 .6) T he results of this code,. 10. CHAPTER 7 ■ THE F# LIBRARIES 166 7575Ch07.qxp 4/27/07 1:03 PM Page 166 User Interfaces In this chapter, you will look at one of the most common tasks a programmer needs to perform—the art of putting. most useful. The Microsoft.FSharp.Math Namespace The Microsoft.FSharp.Math namespace is designed to enable F# to ensure that the F# libraries include definitions of some of the foundational constructs

Ngày đăng: 05/08/2014, 10:21

TỪ KHÓA LIÊN QUAN