博文

[置顶] 如要试用佳佳计算器,请联系我(2008-9-5 20:25:00)

QQ:3459028

请注明试用软件/


阅读全文(1179) | 评论:1 | 复制链接

[置顶] 佳佳科学计算器(2008-9-5 19:47:00)

佳佳科学计算器帮助

 

 

一、用途及功能

该版本的佳佳科学计算器,可用来进行基本数学计算,主要有以下功能:

v 实时显示输入的计算表达式,并显示历史记录

v 基本的加、减、乘、除运算

v 可进行三角函数(正弦、余弦、正切函数及其反函数和双曲函数)、对数(任意对数、以10为底的对数和自然对数)、指数(任意幂)、排列组合(全排列、选排列和组合)、角度弧度、求余数、比较大小、统计等运算或换算功能。

v 可以复制粘贴任意结果或表达式

v 支持科技计数法表达

v 支持连续运算符

v 支持统计运算

其它功能目前尚不具备,在以后版本的开发中将陆续开放和升级。

二、使用说明

1、安装

操作系统:Windows98xp的操作系统,需要安装Windows .NET Framework 2.0或者更高版本。

安装方法:在安装有.NET Framework 2.0的条件下,点击安装文件即可安装,并在桌面放置一个快捷方式。

2、界面说明如下图:

 

  

3、基本运算

按照正规的数学表达式书写要求编辑表达式,点击=号,即可执行计算,执行计算的顺序。

如以下就是正确的表达式:

1-0.001^0.2+pow(2,1/3)*sin(-1)/0.25e3-log(3,5)/log(10)-ln(10)+.0002/0.2

如果表达式不正确,就会在结果显示区显示错误信息。表达式不正确包括:单步计算出现无穷大、数值不在函数定义域内、出现违法字符等等。

重要提示:本计算器支持含有连续运算符的表达式,如1--sin(-1)1*-sin(-1)*2,,1*-sin(-1)^2等等,都可以计算;但是,在使用时要特别小心,因为当出现这种情况时,默认情况下的计算顺序是,从连续运算符的右边开始计算。例如,1*-sin(-1)*2的计算顺序是先求得sin(-1)的值,得到1*--0.841470984807897*2,然后计算--0.841470984807897得到1*0.841470984807897*2;又如,1*-sin(-1)^3的计算顺序是,先计算sin(-1)得到1*--0.841470984807897^3,然后计算--0.841470984807897得到1*0.841470984807897^3,即相当于1*(-sin(-1))^3因此,对于复杂的表达式,建议使用pow(,)进行幂的运算,多使用()将可能出现正负号的表达式括起来。真诚欢迎大家在使用中举报错误。

4、函数运算

除了三角函数及其反函数、双曲三角函数的运算外,下面主要介绍本计算器使用的函数的规定;所有函数运算必须将运算的数或表达式用括号括起来。

Log():执行正数的以10为底的对数的运算,如log(100)=2

Ln():执行以自然数e为底的对数的运算,如ln(10)=2.30258509299405

Log(,):执行以任意数为底的对数的运算,如log(3,5)=0.682606194485985

Exp():执行以自然数e为底的幂的运算,如exp(1)=2.71828182845905

Pow(,):执行任意两个数的幂的运算,如pow(2,5)=32,相当于2^5

Sqrt():执行任意非负数数的开平方运算,如sqrt(36)=6

N!(n):执行正整数n的全排列运算n!,如n!(10)=3628800;如果是小数或负数会出错。

Npr(n,k):执行正整数nk的选排列n!/(n-k)!,nPr(9,7)=181440,其解决了“使用数字19,可以产生多少个不同的7位数,且在同一个7位数字中的数字不可以重复”此类数学问题。

Ncr(n,k):执行组合运算n!/(k!(n-k)!), nCr(10,7)=120,其解决了“试求10个人能组织出多少种不同的7人组(或3人组)”此类数学问题。

Rad():将角度值换算为弧度值,如rad(60)=1.0471975511966,即π/3

Deg():弧度值换算为角度值,如deg(π/4)=45

\:求余运算符,可以求两个正整数相除的余数,如7\4=3,前一个数要大于后一个数

Stat():统计运算,其格式必须是stat(num1,num2,num3)的形式,每个数之间用英文逗号分开;而且不能进行其它任何运算,如1*stat()会导致错误。

三、输入范围

v 输入的数字,单步计算结果和最后计算结果都必须处于-1.79769313486232e308 +1.79769313486232e308之间

v 三角函数、对数和开方、排列组合必须符合定义域要求

v 计算中常数的取值π=3.14159265358979e=2.71828182845905

以上规则必须恪守,否则提示错误。

四、感谢

感谢郑州大学化学系(原化学化工学院)老师的培养教育!

感谢同事、朋友及网友的关心帮助!

感谢家人的支持!

作者联系方式iamben250@126.com  博客iamben250.programfan.com

在佳佳即将上幼儿园时,谨以此向其表示祝福

 


阅读全文(2221) | 评论:0 | 复制链接

[置顶] 本人编写的可完成表达式计算的“佳佳科学计算器”,欢迎试用。(2008-9-3 15:50:00)

本人在工作之余,试着编写了“佳佳科学计算器”,特点是可进行表达式计算,欢迎大家试用。

该计算器有以下功能:

1、支持任意长度表达式计算,字符串表达式可以包含任意的指定函数。

2、支持科学计数法

3、可进行统计运算。

 


阅读全文(1331) | 评论:1 | 复制链接

早上半个多小时学习F#(2012-1-15 9:50:00)

简单搜集了这方面的资料

保存下来

希望对大家有用


阅读全文(89) | 评论:0 | 复制链接

F#介绍及与VB.NET等语言的区别与联系(英文)(2012-1-15 9:44:00)

http://www.developerfusion.com/article/122079/intro-to-f-programming/

Intro to F# Programming

Microsoft took something of a risk with the Visual Studio 2010 release, adding the first brand-new “out of the box” language to the .NET ecosystem since its introduction of C# back in 2001. I speak, of course, of VisualF#, an object/functional hybrid language based on work done by Microsoft Research. Providing both leading-edge functional programming and familiar object-oriented capabilities, F# gives the developer more power to solve problems -- all in fewer lines of code, thanks to succinct syntax.

For developers who have never looked at F# before (a vast majority of the .NET developer population), this article takes a quick glance at the F# syntax. I want to give the F# neophyte enough familiarity with the language to understand code samples in other, more in-depth, articles. Full disclosure dictates, however, that F# is a rich and powerful language, and cannot be explained in all nuance in a single article. The concepts and reasoning of functional programming can only be hinted at in this space; more articles and demonstrations of the power of the “functional” side of F#’s “object/functional hybrid” nature will appear on this website in the weeks and months to come. Welcome to the tip of the iceberg.

Basics

Consider the following F# program, which I created using the Visual Studio “F# Application” project template that ships with Visual Studio 2010:

let msg = "Hello, F#!" System.Console.WriteLine(msg)

Now ponder the following truths about F#:

.NET Accessibility. Observe that F# is capable of calling into the .NET Base Class Library (BCL), as evidenced by the above use of the Console class. F# comes with its own libraries, of course (including the “printfn” function, which serves much the same purpose as WriteLine), but has full access to the entirety of the BCL, including WPFWCF, Workflow, System.Xml, System.Web, and any other assembly accessible to C# or VB.NET. Referencing them uses the same “Add Reference” mechanism in Visual Studio as any other .NET project.

.NET Assembly. Careful inspection of the compiled artifact, using a tool such as Reflector or ILDasm, reveals that the F# compiler produces an executable assembly similar to those produced by C# or VB.NET. Although the structure of the IL generated from the F# compiler looks somewhat different from that produced by the C# or VB.NET compilers, it is still IL, and therefore any library produced by F# is fully accessible from C# or VB.NET. There is even an F# Library project template in Visual Studio. In formal .NET terminology, this and the previous paragraph mean that F# is a “CLI Consumer” (able to consume other CLI assemblies) and a “CLI Producer” (able to produce assemblies for use by other CLI languages).

Strongly-typed, type inferenced. F# is strongly-typed, like C#, meaning that attempts to use the “msg” value defined above as an integer will yield compiler errors—the compiler knows that “msg” must be a string (a System.String, to be precise) because it infers that from the right-hand side of the assignment. However, unlike C# which can only do this for “var”-declared local variables, F# can do this type inference throughout the entire program (with some exceptions), which has the nice effect of reducing the amount of code that must be typed and read without losing the benefits of strong type-checking at compile-time.

Top-level declarations. Contrary to the C# equivalent of this program, F# does not require the programmer to define a class and a static Main() method to serve as the entry point, although it is perfectly happy to allow the developer to do exactly that. F# by default assumes that the first line in the file is to be executed, and unless told otherwise will do precisely that. More importantly, F# permits top-level declarations of values without requiring a class surrounding it, which can simplify certain code scenarios immensely.

(To be strictly correct, F# then takes the code written above and enfolds it inside a class-like declaration that matches the name of the file, so technically this code is inside a class named “Program”, matching the default “Program.fs” filename. This is of interest to those looking to call F# from C#.)

More Basics

A “Hello world” program, regardless of how familiar, offers only limited analysis opportunities. A slightly more complex program, which provides dietary advice while illustrating arrays and looping:

let quantityOfCookies = [| 1; 2; 3; 4; 5; 0; |]
let zero = quantityOfCookies.[5]
quantityOfCookies.[5] <- 6for i in quantityOfCookies do if i = zero then
  printfn "Good job. Eat healthy food." else if i = 1 then
  printfn "One cookie won’t hurt you." else if i = 2 then
  printfn "Two cookies is a nice snack." else   printfn "%d cookies is too many!" i

This is clearly an equally trivial program, but demonstrates several things at once:

Arrays. Arrays are available in F# (though generally discouraged in favor of lists, which will be seen next), and behave much as they do in C# or VB.NET, although the declaration is different, as seen in “quantityOfCookies”. Accessing the array is also slightly different from C# or VB.NET, in that the “.” must precede the square brackets. In keeping with the CLR platform as a whole, arrays are 0-indexed, as they are in C#.

Decision-making. A single equals (“=”) is used to test for equality in if/then branching, not the double-equals (“==”) familiar to C# programmer. Assignment in F# is done using the leftward-arrow operator, as seen above where the sixth element of quantityOfCookies is set to the value 6. (This ability to in-place modify the contents of the array is part of the reason arrays are discouraged in F#, and in functional languages as a whole. It is considered bad form to change the value in a functional language, leading many to refer to this kind of assignment as a destructive update.)

Looping. F# supports while and do/while loops, just as it does the “for” loop demonstrated above. As can be seen, one form of the “for” loop is to iterate across the contents of an array or list or other collection, just as “foreach … in” does in C#. Other forms are available, including straight integer range steps, but this is one of the more common idioms using it.

Indentation-sensitivity. The F# language does not use any sort of paired parentheses or “begin”/”end” markers to denote blocks of code; instead, it is indentation-sensitive, in that the indentation of code offers declaration of what code is in which nested block, much like the scripting language Python. Visual Studio will assist with ensuring the indentation is correct, but in general F#ers feel that the indentation becomes intuitive and falls away from notice before long.

We can tighten up our dietary advice program using a powerful feature of F# known as pattern-matching. It is well beyond the scope of this article to describe pattern-matching in its complete depth, but the following example gives some idea of its use:

for i in [1 .. 6] do     let printMsg = 
        match i with
        | 0 -> "Good job. Eat healthy food."         | 1 -> "One cookie won’t hurt you."         | 2 -> "Two cookies are a nice snack."         | n when n > 2 -> n.ToString() + " cookies are way too many!"         | _ -> failwith("Number out of expected range!")
    printfn "%s" printMsg

The pattern-match construct, on the surface, looks vaguely similar to a switch/case from C#, in that we see a test condition on the left, with a body of code on the right, separated by an arrow. Multiple match clauses are separated by a vertical pipe (“|”). However, pattern-matching can also bind the value being matched (“i”, each time through the loop) into a local value for use in the body of the match clause, such as the use of “n.ToString()” in the second-to-last clause. In that same clause we also see a guard condition: because we don’t want to match negative values, an added “when” clause restricts the match to “n” only when “n” is greater than 3. When present, a guard condition must be met in order for the match to occur. Finally, matching against “_” is a wildcard match, much as “default” works in C#. Note also that a pattern-match is an expression, and thus yields a value (the string constants declared in the body of the match clauses), allowing it to be assigned to the local “printMsg” value for later use. In the last/default case, the “failwith” expression generates a standard .NET exception.

Pattern-matching is used far and wide within F# code, and any student of the language would be well-advised to be comfortable with its myriad capabilities.

Objects

From its inception, F# embraced the “object-oriented” in its nature as much as its functional abilities. This means it can define classes that are every bit as “O-O”-ish as their C# counterparts. Defining a class type in F# can, in many cases, be even shorter than C# equivalents, owing to the type-inference and indentation-sensitivity. A class representing an individual human and the derived subtype describing a student of some educational facility looks like the following:

type Person(first : string, last : string, age : int) =
    member p.FirstName = first
    member p.LastName = last
    member p.Age = age
    member p.SayHowdy() =
        System.Console.WriteLine("{0} says, 'Howdy, all'!", p.FirstName)
    override p.ToString() =
        System.String.Format("[Person: first={0}, last={1}, age={2}]",
            p.FirstName, p.LastName, age)
let jess = new Person("Jessica", "Kerr", 33)
jess.SayHowdy()
type Student(first : string, last : string, age : int, subj : string) =
    inherit Person(first, last, age)
    let mutable subject = subj
    member s.Subject
        with get() = subject
        and set(value : string) = subject <- value     override s.ToString() =
        System.String.Format("[Student: subject={0} {1}]", 
            s.Subject, base.ToString())
    member s.DrinkBeer(?amt : int, ?kind : string) =
        let beers = match amt with Some(beers) -> beers | None -> 12
        let brand = match kind with Some(name) -> name | None -> "Keystone"         System.Console.WriteLine("{0} drank {1} {2}s!",
            s.FirstName, beers, brand)
let brian = new Student("Brian", "Randell", 42, "VB.NET")
brian.DrinkBeer(6)
brian.DrinkBeer(12, "Coors")
brian.DrinkBeer()

The structure of the code is similar in many ways to C# or VB.NET: a type name encloses a series of declarations of members on the type. F# does this without explicit begin/end brackets, using its indentation-sensitive syntax to denote scope. Other differences:

Constructors. Unlike C# or VB.NET which require a constructor to be defined as an explicit member within the class, F# uses the type-declaration line to declare the primary constructor, the one intended to represent the principal means of construction for this class. Any other constructors can be declared inside the class, but will defer to the primary for the actual work, as is generally the case for C# and VB.NET classes, as well.

Immutability. As is common with languages that derive, even in part, from functional roots, F# believes strongly in the notion of “immutability”, that once a value has been set, it cannot be changed. This is true both in the local variables “jess” and “brian”, as well as in the default declarations within the class Student. In fact, to help signify this notion of immutability, “jess” and “brian” will often be referred to as local “values”, not “variables”, since their values cannot vary. (These are also sometimes called “bindings” or “value bindings”, as in “binding a name to a value”.) When working with mutable values, assigning a new value requires use of the “left-arrow” operator, written as “<-“. This is sometimes called the “destructive update” operator (mentioned earlier), both since it “destroys” the value stored there previously and because the negative connotations remind programmers that immutability is better than mutability.

Members. F# uses the “member” keyword to denote the declaration of a public member—method, field, property, event, and so on —as opposed to the “let” syntax to declare an internally-accessible field. All the properties declared in Person are read-only, in keeping with F#’s “immutable values by default” mantra. A read-write property is demonstrated in Student: declare the property using the extended “with get() = … and set() = …” syntax to define get and set method blocks. In addition, since writable properties generally require mutable places to which to write the new values, declaration of the mutable “subject” field is also required. Again we see the destructive update syntax “<-“ to do the assignment. Only fields declared with “let” can be mutable, and these are always private to the type. Members are public unless declared private.

Self-identifiers. Glance at the member declarations: each has a “prefix” in front of the member name (such as “p.FirstName” in Person). This is the “self-identifier” -- the F# developer to choose the name of the reference to object upon which the method is being invoked. For the C#-trained, it may be helpful to write the Person definition of ToString() slightly more C#-ish:

    override this.ToString() =
        System.String.Format("[Person: first={0}, last={1}, age={2}]",
            this.FirstName, this.LastName, age)

Note that the self-identifier has access to all the members of Person; access to private fields or constructor parameters such as “age” is direct. The choice of name for the self-identifier is entirely up to the F# developer; consistency here, however, will be vital to long-term maintenance.

Inheritance. The Student type inherits from the Person class in the same way as any other C# or VB.NET class, by declaring the primary constructor on the “type” line and then using the “inherit” keyword to specify the type from which it inherits (Person). Note that in keeping with traditional inheritance rules in .NET, some constructor on the base type must be invoked, which is done by passing the parameters to the base type declaration. Note as well that to override base-class members, the “override” keyword is required.

Optional parameters. One of the things F# introduces is the ability to supply defaults to method parameters, using the “?” prefix to the parameter name. This means the parameter type is actually an “option” type, which is either a Some() of some value, or None, both of which can be pattern-matched using the syntax above. We’ll look at this in a future article.

Functions

The parts of F# unfamiliar to most .NET developers center on its functional language nature, meaning that functions—executable code—are first-class values alongside data elements. This means that they can be defined inline, defined in terms of other functions (known in some circles as “partial application” of functions), and passed to other functions for execution inside a particular context (the latter of which is known in academic circles as “higher-order functions”). Developers familiar with LINQ will already be familiar with this concept to some degree—“lambdas” are frequently passed in to LINQ expressions, making LINQ itself a series of higher-order functions.

For an example of this style of programming, consider a collection of Person objects defined above:

let people = [ new Person("Ted", "Neward", 40);
               new Person("Charlotte", "Neward", 39);
               new Person("Jessica", "Kerr", 33);
               new Person("Ken", "Sipe", 42);
               new Person("Mark", "Richards", 45);
               new Person("Michael", "Neward", 17); ]

In itself, this list of Persons (formally declared in F# as a “Person list”, as hovering over the declaration of “people” in Visual Studio will reveal) is interesting, but often programmers will want to deduce things about this particular group of people. For example, the goal may be to determine what the total age of all these people, as perhaps as the first step towards determining the average age. While this can easily be done using a “for” loop, as in:

let mutable totalAge1 = 0for p in people do     totalAge1 <- totalAge1 + p.Age;

… it turns out that this is a common operation to do. Writing this “for” loop over and over again is a violation of the Don’t Repeat Yourself principle -- not so much the actual summation of everybody’s age, but the deeper operation: that of iterating across a list and performing some operation on each element in the list, obtaining a result, and combining that with prior results. Abstracting this is a good example of how functional programmers think. We can separate the loop from the details of the operation inside:

let addAges (currentTotal : int) (p : Person) = currentTotal + p.Age
let iterateAndSum (people : Person list) =
    let mutable totalAge = 0
    for p in people do         totalAge <- addAges totalAge p
    totalAge

Notice a concise syntax: the last expression in a method determines the return value. Next, the actual “work” done of obtaining the result will need to be varied across each particular usage. The addAges function becomes an argument to a function that can do more than just sum:

let addAges (currentTotal : int) (p : Person) = currentTotal + p.Age
let iterateAndOperate (people : Person list) (op: int -> Person -> int) =
    let mutable totalAge = 0
    for p in people do         totalAge <- op totalAge p
    totalAge
let totalAge2 = iterateAndOperate people addAges

When the parameter “op” is passed in, it provides the behavior to execute to add the ages together (in the above example). The declaration for “op” is a bit strange, owing to F#’s history, but should be read as “a function taking an int and a Person a yielding an int result”. We now have an example of a higher-order function, because it takes a function to execute as part of its execution—iterateAndOperate takes the “op” function (“addAges” in the above case) as the operation to perform as necessary during the execution of iterateAndOperate.

With this, we can perform any cumulative integer operation on a Person. But why should this operate only on Persons? and only output an integer? If the iterateAndOperate function is written to take any generic type, as well as any appropriate starting value, it looks like:

let iterateAndOperate (seed : 'b) (coll : 'a list) (op : ('b -> 'a -> 'b)) =
    let mutable total = seed
    for it in coll do         total <- op total it
    total
let totalAge2 = iterateAndOperate 0 people addAges

Several things are happening here. First, the explicit declarations of “int” and “Person” have been replaced bytype parameters “’a” and “’b”, which make this function a generic function, able to take a list of any type “a” and iterate-and-operate on it to produce a single value of type “b”. Then, because the starting value, which was 0 in the earlier form, can’t always be assumed to be 0 (particularly since the ‘b type may not even be numeric), that gets passed in as well.

In itself, this hardly seems remarkable. After all, most business programming doesn’t really involve adding up numbers like this, or if it does, it uses Excel to do it. Most business programming does things far more difficult, like transforming a collection of model objects into XML for transmission across the wire as part of a Web services call, for example.

…which, as it turns out, is exactly what iterateAndOperate is capable of doing, as well:

let personToXML (currentXML : string) (p : Person) =
    currentXML + "<person>" + p.FirstName + "</person>" let peopleXML = (iterateAndOperate "<people>" people personToXML) + "</people>" System.Console.WriteLine(peopleXML)

…or, as it is more commonly written, passing in an anonymous function at the point of call:

let peopleXML2 = 
    (iterateAndOperate "<people>" people 
        (fun (curr : string) (p : Person) -> 
            curr + "<person>" + p.FirstName + "</person>")) + "</people>" System.Console.WriteLine(peopleXML2)

Note the use of the “fun” keyword (leading many F# speakers to note how F# has put the “fun” back into programming) to define a function inline, which can be useful in a number of cases where the function being defined will only be used once. Naturally, if a particular snippet of code is in fact written more than once, it can (and should) be given a name and reused across several points of use, rather than defined multiple times.

This particular example is so common that it has its own built-in function. “fold” does exactly what the iterateAndOperate function defined above does:

let peopleXML3 = (List.fold (fun (curr) (p : Person) -> 
                        curr + "<person>" + p.FirstName + "</person>") 
                    "<people>" people) + "</people>" System.Console.WriteLine(peopleXML3)

There are many other things that can be done with functions besides passing them as arguments. If this sounds fun to you, look up partial application and currying.

Summary

In this article, F#’s class definitions, branching and decision-making, pattern-matching, and higher-order function capability have seen the stage, but many other features remain as yet unrevealed. It is the hope of this author that there has been enough here to entice the curious (and the unwary) into spending more time investigating those depths.

F# is a language that embraces multiple language design paradigms within its borders, and attempts to pigeonhole it as a solely-functional or solely-object language will blind potential users to much of its power. Anything that can be done in C# can be done in F#, and work done in F# can be called from C#. The concise syntax is appealing, and great power lies in the function-manipulation capabilities available nowhere else in .NET. While it is certainly possible to use F# to write object-only programs, and while that may be a good path by which to get started learning F#, the greatest benefit will arise from using it the way it was intended: as a mix of the best of both the object-oriented and functional programming worlds.

Jessica Kerr is a consultant with Daugherty Business Solutions in St. Louis, MO. She forges programming and editing skills into a blade which impales bugs and slices run-on sentences into small pieces.

Ted Neward is an Architectural Consultant with Neudesic LLC, the co-author of “Professional F# 2.0” (Wrox), and believes that “he who knows the most programming languages, wins”. Or something like that. Visit his website at http://www.tedneward.com for more details on his activities, or read his blog athttp://blogs.tedneward.com. He resides in Redmond, WA, with his wife, two sons, two cats, one dog, and eight laptops.


阅读全文(134) | 评论:1 | 复制链接

F#编译器和核心库开放源代码(2012-1-15 9:43:00)

F# compiler and core library now open source

Early this morning Microsoft made available a package of compiler and library source code for F#. F# is a functional programming language that runs on the .NET framework. While it is not as mature as C# or VB, the languages more often used to build for .NET, it has progressed quickly and is extremely popular in development environments where functional paradigms have been traditional, for example banking and finance.

The release has been made available through the F# Power Pack, an open-source CodePlex project that previously solely hosted a set of additional libraries for F#. These additional libraries provide features such as LINQ library support; an HTML documentation generator; lexer and parser helpers; and other features for migrating between F# versions. The additions to the project are the code from the F# compiler (for the latest version of F#, 2.0) and the code for the core libraries. Binaries for these are not included, but are still freely available from the official F# website. The main F# release cycle coincides with Visual Studio versions, and Don Syme of the F# team says that new releases of the code will be made available to coincide with future service packs and versions of Visual Studio.

The code itself is on the F# CodePlex project, or you can download binaries from the F# site; more on the release on the F# blog.


阅读全文(120) | 评论:0 | 复制链接

F# 程式設計入門 (2)(2012-1-15 9:41:00)

作者:蔡學鏞

由於 F# 是相當新的技術,目前的學習資源不多。如果你想好好學習 F#,就必須閱讀 APress 出版的《Expert F#》,這是一本好書。我的許多解說範例取自這本書。

第一個範例程式

讓我們先來看一個例子。

#light
// 分析字串內重複出現的字
let wordCount text =
    let words = String.split [' '] text
    let wordSet = Set.of_list words
    let nWords = words.Length
    let nDups = words.Length - wordSet.Count
    (nWords,nDups)

let showWordCount text =
    let nWords,nDups = wordCount text
    printfn "--> %d words in the text" nWords
    printfn "--> %d duplicate words" nDups

利用 F# 的互動環境(fsi.exe),輸入上面的程式,會得到下面的結果:

val wordCount : string -> int * int
val showWordCount : string -> unit

每個值都有型別

意思是,定義了兩個值(val),分別是 wordCount 與 showWordCount,它們的型別分別是「string -> int * int」與「string -> unit」。「string -> int * int」意思是:需要一個字串(string)當參數,傳出值是兩個 int 所組成的值組(tuple)。「string -> int * int」意思是:需要一個字串(string)當參數,沒有傳出值。unit 型別相當於許多編程語言的 void。

利 用 let 定義 wordCount 時,在等號前面有出現「text」,這就是字串參數;而 wordCount 定義的最後,有出現「(nWords,nDups)」,這就是傳出值,它是兩個整數(int)所組成的值組(值組的每個元素用英文逗號隔開)。定義函數的 時候,最後一個出現的值,就是傳出值。

F# 內的每個值都有型別(type),可能是 int、string 或其他,當型別中出現箭頭(->)的時候(例如上面的 wordCount),表示這個值是一個函數,箭頭左邊的是參數的型別,箭頭右邊的是傳出值的型別。如果型別中出現兩個以上的箭頭,則箭頭所隔開的每個部 分都是參數的型別,只有最右邊的是傳出值的型別,例如:「string -> int -> unit」,表示此函數的參數有兩個,分別是字串與整數,且沒有傳出值。

型別推論

你 可能會覺得奇怪,我們並未宣告 text 的型別為字串,也沒有宣告 nWords 與 nDups 的型別為整數,為何 F# 會知道它們的型別?這就是型別推論(Type Inference) -- 根據程式的內容推斷出型別。text 被當作 String.split 的第二個參數,而這個參數必須是字串,所以 F# 知道 text 是字串。而 nWords 是字串的 Length 傳回值,這個傳回值必須是 int,所以 nWords 的型別是 int。而 nDups 是兩個 int 相減的結果,自然也是 int。

透過 Type Inference,我們寫程式時不需要宣告型別,相當方便。但是某些地方,F# 如果無法順利進行 Type Inference,我們就必須標明型別,否則編譯器或互動環境會提出警告。

用 let 進行繫結

利 用 let 的語法,可以將一個名稱繫結(bind)到一個值。例如「let a = 1」表示把 a 繫結到 1。上面的例子中,我們做了七次的 let 繫結。其中只有 wordCount、showWordCount 是全域的(global),其他都是區域的(local)。因此你可以在外面使用 wordCount、showWordCount,卻不能在外面使用 words、wordSet。

要注意繫結的先後次序,例如 wordSet 有用到 words,所以要先繫結 words,再繫結 wordSet。showWordCount 有用到 wordCount,所以要先定義 wordCount,在定義 showWordCount。

特別值得注意「let nWords,nDups = wordCount text」,等號左邊是一個名稱值組,而不是一個名稱,這表示會進行模式比對(Pattern Matching),比對成功才繫結。例如「let a, b = 1, 2」,就會將 a 設定為 1,且將 b 設定為 2。

透過 #light 簡化語法

為 何 wordCount 是全域的,但 words、wordSet、nWords、nDups 卻是區域的,從上面的程式碼中不難看出,因為區域的定義都有內縮。事實上,因為程式一開始有加上 #light,表示內縮也是語法的一部份。如果沒寫 #light,那麼程式內縮也沒用,而是必須明確地在 let 敘述的最後加上「in」,才表示是區域的定義。以上面的例子來說,就是這樣:

// 沒有 #light,下面的例子內縮也沒有用,且一定要加上 in
let wordCount text =
let words = String.split [' '] text in
let wordSet = Set.of_list words in
let nWords = words.Length in
let nDups = words.Length - wordSet.Count in
(nWords,nDups)

當然我們會比較偏向於使用 #light 的方式。使用 #light,除了 in 之外,還有「;;」、done、begin、end 等關鍵字,都可以省略不寫,而是透過內縮表示它們之間的關係。

呼叫函數與 Property

這個範例中,我們呼叫了 String 模組(module)的 split 函數、Set 模組的 of_list 函數、還有 printfn 函數;我們也使用到 String 物件的 Length property 與 Set 物件的 Count property。不管是取用模組內的函數,或物件內的成員,都是利用「.」。

呼叫函數時,如果需要傳入參數,不需要寫括號,而是直接把參數寫在後面。例如「f a b」,表示呼叫 f,傳入「兩個」參數,a 當作第一個參數, b 當作第二個參數。如果你寫成「f (a, b)」,那麼就變成,呼叫 f,傳入「一個」參數,這是「由 a 與 b 構成的」值組。兩者的意義不同。

printfn 是格式化的輸出字串,並換行。C 語言的使用者應該相當熟悉它。

值組與清單

將元素用逗號隔開,組成的資料結構,就是值組(tuple),例如 (nWords,nDups),型別為 int * int。事實上,值組內部的元素可以是不同的型別,例如 (1, "F#") 是個值組,其型別為 int * string。

F# 也很常使用清單(list),清單內的元素型別必須一致,且使用分號隔開,前後使用方括號,例如 [ 2; 3; 5; 7; 11],型別是 int list 也可以寫成 list。本範例中,當作 String.split 第一個參數的 [' '] 其實正是一個 list,只不過它只有一個元素(空白字元' '),如果是空的清單則寫成 [ ]。

第二個範例程式

接下來看第二個範例程式:

open System.Windows.Forms

let form = new Form(Visible=true,TopMost=true,Text="Welcome to F#")
let textB = new RichTextBox(Dock=DockStyle.Fill, Text="Here is some initial text")
form.Controls.Add(textB)

open System.IO
open System.Net

// 取得網頁內容
let http(url: string) =
    let req = System.Net.WebRequest.Create(url)
    let resp = req.GetResponse()
    let stream = resp.GetResponseStream()
    let reader = new StreamReader(stream)
    let html = reader.ReadToEnd()
    resp.Close()
    html

textB.Text <- http("http://news.bbc.co.uk")

使用 .NET API

這個範例中,我們使用到 .NET 的 WinForms、I/O、Networking API。再用到這些 API 之前,我們先透過 open,表示要使用它,這樣可以讓我們不用寫全名(full-qualified)。

open System.Windows.Forms
open System.IO
open System.Net

產生物件

產生物件一樣是透過 new 關鍵字來達成。如下所示:

let form = new Form(Visible=true,TopMost=true,Text="Welcome to F#")

從上面的例子來看,你可能以為 Form 類別有定義一個建構子(constructor),此建構子具有三個參數,分別是 boolean 表示是否 Visible、boolean 表示是否 TopMost、以及標題文字。如果你去查一下 Form 的說明文件,會找不到這樣的建構子。既然如此,上面又怎麼能這麼建構出 Form 物件呢?

從上面的例子來看,你可能以為 Form 類別有定義一個建構子(constructor),此建構子具有三個參數,分別是 boolean 表示是否 Visible、boolean 表示是否 TopMost、以及標題文字。如果你去查一下 Form 的說明文件,會找不到這樣的建構子。既然如此,上面又怎麼能這麼建構出 Form 物件呢?

let form = new Form ()
form.Visible <- true
form.TopMost <- true
form.Text <- "Welcome to F#"

設定 Property

上面用到「<-」,整個程式的最後一行也出現「<-」,這是指定(assignment)的意思。繫結(binding)和指定是不一樣的,繫結只能進行一次,使用的語法是「let a = b」,但指定可以指定多次,使用的語法「a <- b」。可以被繫結者,顯然必須是「不可被改變的」(immutable);而可以被指定者顯然必須是「可被改變的」(mutable)。.NET  物件的 Property 是可以被改變的(除非唯讀者),所以用指定的方式。

註明型別

之前提到過「型別推論」讓我們可以不必標明型別,但有些時候你想要(或需要)主動註明型別,寫法是這樣「名稱 : 名稱」。

在上面的範例定義 http 函數的時候,就有註明其參數 url 必須是字串,所以寫成這樣:

let http(url: string) = …

最後…

這篇文章利用兩個範例,詳細解說其中出現的語法,讓你對於 F# 有大概的認識。下一次的文章,會開始從最基本的一切說起,包括 F# 的各種型別、各種運算子、以及一些基本的 FP 語法。

http://msdn.microsoft.com/zh-tw/library/dd252674.aspx


阅读全文(132) | 评论:0 | 复制链接

F# 程式設計入門 (1)(2012-1-15 9:40:00)

作者:蔡學鏞

大家對函數編程(Functional Programming,FP)相當陌生,少有人能正確地敘述出函數編程是什麼,有什麼好處。函數編程長期以來沒有出現在主流的商業軟體世界,欠缺主流語言的支援。

一直以來,真正讓 FP 無法被接受的原因可能是「執行效率」。傳統上,函數式編程語言的效率確實比命令式(imperative)編程語言來得差,這在商業系統上是不能忍受的。命令式語言讓我們用貼近馮紐曼架構(van Neumann Architecture)機器的方式寫程式,比較低階,所以效率會比較高。

但是這個原因卻有了變化。過去這十多年,我們先是對「跨平台」和「反微軟」的重視超越「執行效率」,而後又開始重視「簡單」、「快速開發」。想要「簡單」、「快速開發」,就要用比較高階的抽象,因此函數式編程比命令式編程更適合現在的開發環境。

這些年來硬體的進步,讓我們對於函數編程的效率不再是大問題;甚至由於編譯技術的進步,函數式編程語言的執行速度,現在也已經不再是吳下阿蒙。妙的是,還 不只這樣,局勢似乎 180 度反轉成為對 FP 有利的局面:多 CPU、多核心、超執行緒(HT)的硬體架構普及,以及分散式運算的流行,這根本就是專為滋養 FP 繁殖而打造的環境。

1930 年代,Alonzo Church 開發出一套 formal system,名為 lambda calculus。這個系統本質上是一個編程語言,為一部「想像中的機器」所設計的語言。lambda calculus 的函數可以接受函數當作輸入(引數)和輸出(傳出值)。這樣的函數用希臘字母 λ 當作識別,所以這個語言才名為 lambda calculus。

Alan Turing 也在做類似的研究,開發出不同的系統,就是大名鼎鼎的 Turing machine,他得到的結論和 Alonzo Church 類似。後來證實 Turing machine 和 lambda calculus 的威力一樣強大。

1958 年,對 lambda calculus 相當感興趣的 MIT 教授 John McCarthy 設計出 Lisp 語言,Lisp 實踐了 lambda calculus,讓 lambda calculus 可以在 von Neumann 電腦上執行!大家開始注意到 Lisp 的威力。1973 年,MIT 的人工智慧實驗室開發出所謂的 Lisp machine 硬體,等於是將 lambda calculus 的機器實踐出來了!

只要遵守 FP 的原則,管他用什麼語言,都可以進行 FP。你可以用非函數式的語言(例如 Java),進行 FP;正如同你可以用非物件導向的語言(例如 C),進行 OOP 一樣。但是只有想不開的人才會這麼做,畢竟事倍功半。

LISP 是第一個函數式語言,越來越多函數式語言隨之出現。真實世界的函數式語言無法像 Lambda Calculus 那樣,畢竟 Lambda Calculus 是讓虛幻不存在的機器執行的,沒有受到真實世界的限制。所以函數式語言雖然都是源自於 Lambda Calculus,但是卻都和 Lambda Calculus 之間存在差異。由於 FP 只是一些構想,各種語言實踐這些構想的作法,彼此之間也可能有不小的差異。

儘管各種語言有差異,但是大致上來說,FP 的特點在於:

  • 「沒有副作用」(Side Effect)。在表示式(expression)內不可以造成值的改變。
  • 「第一級函數」(First-Class Function)。函數被當作一般值對待,而不是次級公民,也就是說,函數可當作「傳入參數」或「傳出結果」。

基本上,遵守上述兩點進行程式編寫,差不多就可以稱為 FP。FP 和我們慣用的編寫程式風格,有相當大的差異。Imperative Programming 認為程式的執行,就是一連串狀態的改變;但 FP 將程式的運作,視為數學函數的計算,且避免「狀態」和「可變資料」。

為了提昇效率,許多函數式語言會納入 imperative 的某些作法(例如允許副作用),這類的 FPL 被稱為不純(Impure)的函數式編程語言,例如 Ocaml、F#、LISP、REBOL。當然也有一些語言堅持 Pure Functional 的作法,例如 Erlang、Haskell、Occam、Oz。

以往純的函數式語言會被某些人認為不實際,而不純的函數式語言,則被認為比較實際,但是最近大家的看法似乎有了改變。主要是以 Erlang 為首的純函數式語言,似乎更能充分展現出 FP 的優勢。FP 的優勢是容易進行單元測試、容易除錯、適合編寫(Concurrency)的程式。適合進行程式碼「熱抽換」或「熱部署」(Hot Code Deployment)。

除了上述的優點,我們可以透過 FP 作了些什麼,來瞭解 FP 是什麼。FP 語言常常能做下面的事:Higher-Order Function、Currying、Lazy Evaluation、Continuations、Pattern Matching、Closure、List Processing、Meta-Programming。如果你對這些觀念不太瞭解,可以查詢 Wikipedia 的說明。

微軟也注意到了 FP 的潮流。F# 的 F 是 Functional Language(函數式語言)的意思,# 是 .NET 的意思,所以顧名思義,F# 是 .NET 平台上函數式語言,由微軟官方所設計。F# 的血緣關係是 ML -> Caml -> OCaml(Objective-Caml)-> F#。同時 F# 也混入了一些 Haskell 和 C# 的語言特色。

F# 被視為 ML 家族系列的語言,ML 家族的函數式語言都是 Strong-Type、Static-Type,F# 也是如此。和 Common Lisp 與 Erlang 等函數式語言相比,ML 家族的語法比較正常一點,一般語言的使用者可以比較快熟悉它。

F# 雖然是編譯式語言,但是微軟有提供一個 F# 互動式 Console,使得 F# 可以用類似 Scripting 的方式編寫,也有助於學習。因此,如果你想在 Console 寫 .NET 程式,除了可以使用 PowerShell 之外,現在也可以使用 F# 了,且 F# 會比 PowerShell 更適合寫一般的 .NET 應用,因為 PowerShell 是 Shell 語言,但是 F# 是一般目的的編程語言。

Wadler 提出:大家不用函數式語言,原因有七點 Libraries、Portability、Availability、Packagability、Tools、Training、 Popularity。微軟認為 F# 可以解決這七個問題。因此函數式語言目前主要的用途雖然是以科學和財務金融領域為大宗,但是未來可能會吸引一些其他領域的人使用。F# 社群甚至有人認為,只要你用過 F# 語言,你會很難回頭去用以前的語言。我不認為這樣的魅力來自 F# 本身,我認為這樣的魅力是來自 FP(Functional Programming 函數式編程)。

F# 語言試圖整合 FP 和 .NET,讓 .NET 編程也能享有 FP 的諸多好處,但這一點我持否定的態度。想得到 FP 的好處,編寫程式時必須謹守 FP 的作法,不只是你的程式要遵守 FP,連你用到的程式庫也必須遵守 FP 才行,而 .NET Framework 是 OOP + Imperative Programming 的方式設計出來的 API,並不是依據FP的構想而設計出來的 API。

但不要因此小看了 F# 未來的潛力。微軟特別為 F# 在 .NET Framework 上做出一套逼近 OCaml 3.06 的 ML 相容程式庫,以及 F# 專屬的程式庫。寫程式時盡量多使用這些程式庫,少用標準的 .NET 框架,就比較能享有 FP 的優點。例如:使用 ParallelFX,而不要用 .NET Threading API。

你可能會問:既然如此,為何不直接用 OCaml 就好了?因為 F# 是微軟的語言,以微軟豐富的資源,如果有意好好發展 F#,F# 的未來是無可限量的。

F# 雖然強調 FP,但它其實是多重範式(multi-paradigm)的語言,不只是 FP 的語言。FP 的範式包括了 Functional Programming、Imperative Programming、Object-Oriented Programming、Meta-Programming。多重範式的好處是,你可以依據當時的需求,使用最適合的範式;缺點是你往往挑錯範式。因此一般人用 F# 開發出來的程式,能享有多少 FP 範式的優點,這是相當值得懷疑的。

讓我們瞭解一下 F# 語言有哪些優點和缺點。除了 FP 的優點都可以算到 F# 頭上之外,有微軟撐腰也是 F# 的一大優點。微軟已經進行 F# 的開發許多年了,看得出微軟對 F# 是認真的。F# 的缺點是目前似乎還在演化中、還是有一些 bug、目前還在實驗室產品和商業化產品的過渡期、文件寫得不完備。

由於文件寫得不完備,所以想利用網站上的資源學習 F#,會比較辛苦一些。妙的是,寫得比文件更完整的書出卻已經出版了。其中一本「Expert F#」似乎頗受好評,作者是 F# 計畫的最重要負責人 Don Syme。

F# 目前(2008 年五月)最新版是 1.9.4,你可以在 F# 網站上取得。下載回來之後,依照它的說明安裝,你就可以開始使用 F# 了。提醒你,安裝 F# 之前,你需要先安裝 .NET 2.0 的環境。

安裝完 F# 之後,你將具有 F# 程式庫、F# 編譯器 FSC.EXE、F# 互動環境 FSI.EXE。如果你有 Visual Studio,你也可以依照它的說明步驟,讓 F# 和 Visual Studio 整合在一起。用 Visual Studio 寫 F# 程式當然比用記事本 + 命令列來得方便。

提醒你,要先將你的 F# 安裝路徑下的 bin 目錄,加入 PATH 環境變數中。才可以進行後續的動作。

現在就讓我們用記事本和命令列,以最儉樸的方式寫一個 F# 程式:

let x = "Hello World";;
System.Console.WriteLine(x);;

你應該也注意到了,F# 用兩個分號「;;」當作結尾。習慣上,F# 的副檔名是 .fs 或 .ml。將上面的檔案存檔為 Hello.fs。接下來,用下面的方式編譯:

let x = "Hello World";; System.Console.WriteLine(x);; 

編譯成功之後,你會看到 hello.exe 檔案。這是 .NET 的 Managed PE 檔。讓我們執行這個檔案:

C:\>Hello.exe
Hello World 

上面是編譯器的執行方式,接下來,看看如何使用解譯器的執行方式,做同樣的事。執行 FSI,進入它的 Console,做下面的輸入:

> let x = "Hello World";;
val x = string
> System.Console.WriteLine(x);; 
Hello World
val it : unit = ()

顯然每一行程式都會立刻執行,並立刻做出反應。這對於初學者來說,是相當方便的一種學習方式。

在執行完「let x = "Hello World";;」之後,你會看到系統輸出「val x = string」,意思是,現在有一個變數 x,它被繫結(bind)到一個字串。

在執行完「System.Console.WriteLine(x);;」之後,你會看到系統輸出「Hello World」,這是執行結果,然後又輸出「val it : unit = ()」,意思是,現在有一個變數 it,它被繫結(bind)到一個 unit。如果你沒有指定變數,那麼 FSI 會自動將它繫結到變數 it。

離開 FSI 的方式有三種:

  • #quit;;
  • #q;;
  • exit 0;;

F# 可以用來寫一般的 .NET 程式,下面是 F# 版本的 WinForm 程式:

#light
open System
open System.Windows.Forms

let form = new Form()
form.Width  <- 400
form.Height <- 300
form.Visible <- true 
form.Text <- "Hello World Form"

// Menu bar, menus 
let mMain = form.Menu <- new MainMenu()
let mFile = form.Menu.MenuItems.Add("&File")
let miQuit  = new MenuItem("&Quit")
mFile.MenuItems.Add(miQuit)

// RichTextView 
let textB = new RichTextBox()
textB.Dock <- DockStyle.Fill  
textB.Text <- "Hello World\n\nOK."
form.Controls.Add(textB)    

// callbacks 
miQuit.Click.Add(fun _ -> form.Close())

#if COMPILED
// Run the main code. The attribute marks the startup application thread as "Single 
// Thread Apartment" mode, which is necessary for GUI applications. 
[]    
do Application.Run(form)
#endif

如果你懂 WinForm 的話,這個程式應該不需要我的解說。我只說明一些和 F# 有關的重點。一開始的「#light」讓你可以不用在每行程式的最後中加入「;;」。「#light」也會讓「程式內縮」變成語法的一部份(而不只是為了美觀而已)。open 相當於 import。為 property 指定值要使用「<-」運算子。「miQuit.Click.Add(fun _ -> form.Close())」的「fun _ -> form.Close()」就是一個匿名函數,也就是 lambda 函數。函數可以當作 Add 的引數,所以 Add 就是 Higher-Order Function(較高次方函數)。

從上面的程式來看,你可能會感到疑惑,F# 好像和 C# 的差異也不是很大,並沒有特別精簡,這是因為你用 F# 寫 .NET 程式的緣故。如果你使用 F# 自己的程式庫「FSharp.Core」組件與「FSharp.Compatibility」組件,狀況就會不同了。

大多數的人沒有使用過函數編程技術,所以思維會受到傳統 imperative 編程作法的拉扯,一開始很不習慣。只要堅持下去,跨過門檻之後,你會發現,函數編程其實更自然,生產力更高。下次的文章開始介紹 F# 的語法。

http://msdn.microsoft.com/zh-tw/library/dd252673.aspx


阅读全文(143) | 评论:0 | 复制链接

F# 静态解析变量类型(2012-1-15 9:36:00)

http://blog.csdn.net/hikaliv/article/details/4565015 

文 / 李博(光宇广贞)

       《方法多态与Duck typing、C#之拙劣与F#之优雅》文末留了个尾巴,F# 的 inline 与 ^ 算符如何联袂完成方法多类的类型检查的。本文讨论这一问题。

       《F# 手册》§5.1.2 提到:

A type of the form ^ident is a statically resolved variable type. A fresh type inference variable is created and added to the type inference environment (see §14.6). This type variable is tagged with an attribute indicating it may not be generalized except at inline definitions (see §14.7), and likewise any type variable with which it is equated via a type inference equation may similarly not be generalized.

       问题解决了。那范型 'a 与 ^a 的区别呢?差就差在 ^a 需要 inline,见如下示例:

> let id (a : 'a) = a;; 
val id : 'a -> 'a 
> id 1, id "hi";; 
val it : int * string = (1, "hi") // Good, it's generic 
> let id (a : ^a) = a;; 
  let id (a : ^a) = a;; 
  -------------^^ 
stdin(8,14): warning FS0064: This construct causes code to be less generic than indicated by the type annotations. The type variable 'a has been constrained to be type 'obj'. 
val id : obj -> obj 
> id 1, id "hi";; 
val it : obj * obj = (1, "hi") // Constrained to obj - not generic 
> let inline id (a : ^a) = a;; 
val inline id :  ^a ->  ^a 
> id 1, id "hi";; 
val it : int * string = (1, "hi") // Since it's inline, it's generic


阅读全文(104) | 评论:0 | 复制链接

微软F#语言参考手册2010年4月版(英文)(2012-1-15 9:35:00)

http://research.microsoft.com/en-us/um/cambridge/projects/fsharp/manual/spec.html

由于原文太大,无法上传请根据链接学习参考


阅读全文(113) | 评论:0 | 复制链接