NetRoc's Blog

N-Tech

 

2010年9月6日

Flash调用Alchemy编译的代码时出现Error #1506的解决

这个问题困扰了我很久,因为需要频繁的向Alchemy代码中传递大ByteArray数组。当某次传递的数据量较大时,后面再调用时就会抛出下面这个异常:
Error #1506: The specified range is invalid.
比较确定是Adobe在Alchemy实现中的BUG,但是一直没有找到解决办法。碰巧在google上找到某老外回的帖子,似乎原因在于反复创建CInitLib对象,并且多次调用init方法。如果将CInitLib作为全局对象,并且只初始化一次,就可以解决这个问题。
实验了一下,确实如此。很好的解决了我代码中目前存在的问题。在这里稍微记录一下。
Alchemy现在几乎是我的必备了,呵呵
原帖地址在此:http://forums.adobe.com/message/2259918

posted @ 2010-09-06 17:30 NetRoc 阅读(1207) | 评论 (0)编辑 收藏

2010年7月20日

又一VC6编译器BUG

又一VC6编译器BUG

NetRoc

http://www.DbgTech.net/

一个XML解析函数,用于获取XML中的子节点。Debug下面正常,而Release下面始终获取不到节点;另外,代码中有一个没有使用到的CString变量,Release下面如果定义它,则运行正常,如果没有定义,则运行出错。函数代码如下:

//到到第一个Child结点指针

Node* CXMLProtocolParse::GetFirstChild(Node* pNode)

{//    AfxMessageBox("");

    ASSERT (pNode != NULL);

 

    if ( pNode == NULL )

        return NULL;

 

    Node* pElem = NULL;

    try

    {

        pNode->get_firstChild(&pElem);

        if ( pElem == NULL )

            return NULL;

        m_ArrayElement.Add(pElem);

 

        //CString strError ; //不加这个变量的申明 Release中无法正确获得节点数据

 

        DOMNodeType tagNodeType = NODE_INVALID;

        pElem->get_nodeType(&tagNodeType);

        if ( tagNodeType == NODE_TEXT )    //第一个子结点为文本结点, 则把其下一个兄弟结点认为是第一个结点。后来发现问题就出在这个判断对应的跳转语句上

            return GetNextSibling(pElem);

 

    }catch(...)

    {

        ASSERT(FALSE);

    }

    return pElem;        //Release版执行从这里返回时就会出问题,返回值为NULL,但是pElem其实是有值的。

}

未定义CString变量时的汇编代码如下,我们可以跟踪一下出错的过程:

.text:00401D60 push ebp

.text:00401D61 mov ebp, esp

.text:00401D63 push 0FFFFFFFFh

.text:00401D65 push offset unknown_libname_11 ; Microsoft VisualC 2-8/net runtime

.text:00401D6A mov eax, large fs:0

.text:00401D70 push eax

.text:00401D71 mov large fs:0, esp

.text:00401D78 sub esp, 8

.text:00401D7B mov eax, [ebp+arg_0]

.text:00401D7E push ebx

.text:00401D7F push esi

.text:00401D80 push edi

.text:00401D81 xor edi, edi

.text:00401D83 mov [ebp+var_10], esp

.text:00401D86 cmp eax, edi

.text:00401D88 mov esi, ecx

.text:00401D8A jz short loc_401DEC

.text:00401D8A

.text:00401D8C mov ecx, [eax]

.text:00401D8E lea edx, [ebp+arg_0]

.text:00401D91 push edx

.text:00401D92 push eax

.text:00401D93 mov [ebp+arg_0], edi

.text:00401D96 mov [ebp+var_4], edi

.text:00401D99 call dword ptr [ecx+34h] ; pNode->get_firstChild(&pElem);

.text:00401D99

.text:00401D9C mov edx, [ebp+arg_0]

.text:00401D9F cmp edx, edi

.text:00401DA1 jz short loc_401DEC

.text:00401DA1

.text:00401DA3 mov eax, [esi+0Ch]

.text:00401DA6 lea ecx, [esi+4]

.text:00401DA9 push edx

.text:00401DAA push eax

.text:00401DAB     call CPtrArray::SetAtGrow(int,void *) ; m_ArrayElement.Add(pElem);

.text:00401DAB

.text:00401DB0 mov eax, [ebp+arg_0]

.text:00401DB3 lea edx, [ebp+var_14]

.text:00401DB6 mov [ebp+var_14], edi

.text:00401DB9 push edx

.text:00401DBA mov ecx, [eax]

.text:00401DBC push eax

.text:00401DBD call dword ptr [ecx+28h] ; pElem->get_nodeType(&tagNodeType);

.text:00401DBD

.text:00401DC0 mov eax, [ebp+var_14]

.text:00401DC3 cmp eax, 3

                            ;这里因为比较的结果不同,所以将pElem的值传给eax,准备返回,到这个位置都还是一切正常的。但是跳转的目标因为优化的关系,重复使用了前面return NULL;这条语句的代码,所以后面会有问题。

.text:00401DC6 mov eax, [ebp+arg_0]

.text:00401DC9 jnz short loc_401DEC

.text:00401DC9

.text:00401DCB push eax

.text:00401DCC mov ecx, esi

.text:00401DCE call CXMLProtocolParse__GetNextSibling

.text:00401DCE

.text:00401DD3 mov ecx, [ebp+var_C]

.text:00401DD6 mov large fs:0, ecx

.text:00401DDD pop edi

.text:00401DDE pop esi

.text:00401DDF pop ebx

.text:00401DE0 mov esp, ebp

.text:00401DE2 pop ebp

.text:00401DE3 retn 4

.text:00401DE3

.text:00401DE6 ; ---------------------------------------------------------------------------

.text:00401DE6

.text:00401DE6 loc_401DE6: ; DATA XREF: .rdata:stru_4049F8o

.text:00401DE6 mov eax, offset loc_401DEC

.text:00401DEB retn

.text:00401DEB

.text:00401DEC ; ---------------------------------------------------------------------------

.text:00401DEC

.text:00401DEC loc_401DEC: ; CODE XREF: CXMLProtocolParse__GetFirstChild+2Aj

.text:00401DEC ; CXMLProtocolParse__GetFirstChild+41j

.text:00401DEC ; CXMLProtocolParse__GetFirstChild+69j

.text:00401DEC ; DATA XREF: CXMLProtocolParse__GetFirstChild:loc_401DE6o

 

        ;跳转到这个地方返回,多出来的那个xor eax,eax,问题就来了!!!这条语句块本来用作return NULL;返回的,所以里面有清零eax的操作,错误重用的话出大毛病!!!

.text:00401DEC mov ecx, [ebp+var_C]

.text:00401DEF pop edi

.text:00401DF0 pop esi

.text:00401DF1 xor eax, eax

.text:00401DF3 mov large fs:0, ecx

.text:00401DFA pop ebx

.text:00401DFB mov esp, ebp

.text:00401DFD pop ebp

.text:00401DFE retn 4

.text:00401DFE

.text:00401DFE CXMLProtocolParse__GetFirstChild endp

 

 

下面再来看定义了CString变量时的汇编代码,因为多了CString类的构造和析构函数,所以代码的结构发生了改变,return NULL;这条语句的代码没有被重用:

.text:00401D60

.text:00401D60 ; Attributes: bp-based frame

.text:00401D60

.text:00401D60 CXMLProtocolParse__GetFirstChild proc near

.text:00401D60 ; CODE XREF: CXMLProtocolParse__FindNode+73p

.text:00401D60

.text:00401D60 var_18 = byte ptr -18h

.text:00401D60 var_14 = dword ptr -14h

.text:00401D60 var_10 = dword ptr -10h

.text:00401D60 var_C = dword ptr -0Ch

.text:00401D60 var_4 = dword ptr -4

.text:00401D60 arg_0 = dword ptr 8

.text:00401D60

.text:00401D60 push ebp

.text:00401D61 mov ebp, esp

.text:00401D63 push 0FFFFFFFFh

.text:00401D65 push offset unknown_libname_11 ; Microsoft VisualC 2-8/net runtime

.text:00401D6A mov eax, large fs:0

.text:00401D70 push eax

.text:00401D71 mov large fs:0, esp

.text:00401D78 sub esp, 0Ch

.text:00401D7B mov eax, [ebp+arg_0]

.text:00401D7E push ebx

.text:00401D7F push esi

.text:00401D80 xor ebx, ebx

.text:00401D82 push edi

.text:00401D83 cmp eax, ebx

.text:00401D85 mov [ebp+var_10], esp

.text:00401D88 mov esi, ecx

.text:00401D8A jnz short loc_401DA1

.text:00401D8A

.text:00401D8C xor eax, eax

.text:00401D8E mov ecx, [ebp+var_C]

.text:00401D91 mov large fs:0, ecx

.text:00401D98 pop edi

.text:00401D99 pop esi

.text:00401D9A pop ebx

.text:00401D9B mov esp, ebp

.text:00401D9D pop ebp

.text:00401D9E retn 4

.text:00401D9E

.text:00401DA1 ; ---------------------------------------------------------------------------

.text:00401DA1

.text:00401DA1 loc_401DA1: ; CODE XREF: CXMLProtocolParse__GetFirstChild+2Aj

.text:00401DA1 mov ecx, [eax]

.text:00401DA3 lea edx, [ebp+arg_0]

.text:00401DA6 push edx

.text:00401DA7 push eax

.text:00401DA8 mov [ebp+arg_0], ebx

.text:00401DAB mov [ebp+var_4], ebx

.text:00401DAE call dword ptr [ecx+34h] ; pNode->get_firstChild(&pElem);

.text:00401DAE

.text:00401DB1 mov edx, [ebp+arg_0]

.text:00401DB4 cmp edx, ebx

.text:00401DB6 jnz short loc_401DCD

.text:00401DB6

                ;这里return NULL;

.text:00401DB8 xor eax, eax

.text:00401DBA mov ecx, [ebp+var_C]

.text:00401DBD mov large fs:0, ecx

.text:00401DC4 pop edi

.text:00401DC5 pop esi

.text:00401DC6 pop ebx

.text:00401DC7 mov esp, ebp

.text:00401DC9 pop ebp

.text:00401DCA retn 4

.text:00401DCA

.text:00401DCD ; ---------------------------------------------------------------------------

.text:00401DCD

.text:00401DCD loc_401DCD: ; CODE XREF: CXMLProtocolParse__GetFirstChild+56j

.text:00401DCD mov eax, [esi+0Ch]

.text:00401DD0 lea ecx, [esi+4]

.text:00401DD3 push edx

.text:00401DD4 push eax

.text:00401DD5 call CPtrArray::SetAtGrow(int,void *) ; m_ArrayElement.Add(pElem);

.text:00401DD5

.text:00401DDA lea ecx, [ebp+var_18]

.text:00401DDD call CString::CString(void) ; 初始化CString

.text:00401DDD

.text:00401DE2 mov eax, [ebp+arg_0]

.text:00401DE5 lea edx, [ebp+var_14]

.text:00401DE8 mov [ebp+var_14], ebx

.text:00401DEB push edx

.text:00401DEC mov ecx, [eax]

.text:00401DEE push eax

.text:00401DEF mov byte ptr [ebp+var_4], 1

.text:00401DF3 call dword ptr [ecx+28h] ; pElem->get_nodeType(&tagNodeType);

.text:00401DF3

 

                        ;这里比较并跳转

.text:00401DF6 cmp [ebp+var_14], 3

.text:00401DFA jnz short loc_401E29

.text:00401DFA

.text:00401DFC mov eax, [ebp+arg_0]

.text:00401DFF mov ecx, esi

.text:00401E01 push eax

.text:00401E02 call CXMLProtocolParse__GetNextSibling

.text:00401E02

.text:00401E07 lea ecx, [ebp+var_18]

.text:00401E0A mov esi, eax

.text:00401E0C mov byte ptr [ebp+var_4], bl

.text:00401E0F call CString::~CString(void)

.text:00401E0F

.text:00401E14 mov eax, esi

.text:00401E16 mov ecx, [ebp+var_C]

.text:00401E19 mov large fs:0, ecx

.text:00401E20 pop edi

.text:00401E21 pop esi

.text:00401E22 pop ebx

.text:00401E23 mov esp, ebp

.text:00401E25 pop ebp

.text:00401E26 retn 4

.text:00401E26

.text:00401E29 ; ---------------------------------------------------------------------------

.text:00401E29

.text:00401E29 loc_401E29: ; CODE XREF: CXMLProtocolParse__GetFirstChild+9Aj

 

                    ;析构并返回,这里就没有再做eax清零的操作了,所以运行正常

.text:00401E29 lea ecx, [ebp+var_18]

.text:00401E2C mov byte ptr [ebp+var_4], bl

.text:00401E2F call CString::~CString(void)

.text:00401E2F

.text:00401E34

.text:00401E34 loc_401E34: ; DATA XREF: sub_401E4Ao

.text:00401E34 mov ecx, [ebp+var_C]

.text:00401E37 mov eax, [ebp+arg_0]

.text:00401E3A pop edi

.text:00401E3B pop esi

.text:00401E3C mov large fs:0, ecx

.text:00401E43 pop ebx

.text:00401E44 mov esp, ebp

.text:00401E46 pop ebp

.text:00401E47 retn 4

.text:00401E47

.text:00401E47 CXMLProtocolParse__GetFirstChild endp

 

刚开始以为是栈破坏或者变量未初始化的问题,没想到又发现一个VC6编译器BUG,小小的记录一下,呵呵。经测试,用VS2008编译不会有这个问题。10多年前的东西用起来还真是危险啊~~~

posted @ 2010-07-20 11:22 NetRoc 阅读(751) | 评论 (0)编辑 收藏

2010年7月19日

VC6 CRT库一BUG记录

VC6 CRT库一BUG记录

帮一哥们调试了一Dump文件。来源是一服务器端程序,连续运行10多天后出现崩溃。看dump崩溃在MSVCRTD!malloc_dbg内,写了个VC6程序试了一下,崩溃的具体位置如下:

_heap_alloc_dbg(unsigned int 100, int 1, const char * 0x00000000, int 0) line 334

_nh_malloc_dbg(unsigned int 100, int 0, int 1, const char * 0x00000000, int 0) line 248 + 21 bytes

malloc(unsigned int 100) line 130 + 21 bytes

代码如下:

lRequest = _lRequestCurr;

 

/* break into debugger at specific memory allocation */

if (lRequest == _crtBreakAlloc)

_CrtDbgBreak();

因为lRequest和_crtBreakAlloc相等,所以中断在了调试断点处。_lRequestCurr是CRT调试堆用于记录堆内存被申请的次数,每次申请的时候加1,时间长了之后这个变量溢出变成-1,而_crtBreakAlloc默认的值就是-1,因此这里CRT认为被下了用户断点,主动中断。

看来Debug版用于生产环境还是有风险的。合理的还是用Release+调试信息比较好,呵呵。

posted @ 2010-07-19 21:20 NetRoc 阅读(912) | 评论 (0)编辑 收藏

命令行编译AS项目

命令行编译AS项目

BSSB Adobe,设计些程序真SB,感觉像学生的作业,不像商业软件……

单个AS文件的话可以直接使用mxmlc编译出来,但是如果该swf是用来动态加载,内部需要包含一些没有被直接引用的类的话,必须使用compc将大部分文件编译成swc之后,在mxmlc参数中强制包含进去。具体步骤如下:

  1. 假如有三个类:Package1.Package11.Class1、Package1.Package11.Class2 、Package1.Package11.Class3
  2. 首先除开一个文件留给mxmlc,将另外两个文件用COMPC编译成swc

    compc –source-path=.\ -include-classes= Package1.Package11.Class1, Package1.Package11.Class2 –o=lib.swc

  3. 然后用mxmlc编译成swf

    mxmlc –source-path=..\ -file-specs .\ Package1\ Package11\ Class3.as –include-libraries=.\lib.swc

  4. 最终出来的swf中多出了一个乱七八糟的类,原因未知。不过这个问题总算是解决了。

恶心得想吐,太TMD SB了,简直不是人做得出来的事情。再怎么忽悠美工也不能这样啊……

posted @ 2010-07-19 19:03 NetRoc 阅读(2097) | 评论 (0)编辑 收藏

2010年6月20日

F#学习笔记之五 — F#程序的组成

NetRoc

http://www.DbgTech.net

F#和很多语言不同,它不一定具有明确的程序入口点,如C语言的main函数。在单源文件程序中,代码是从头至尾执行的。在多源文件程序中,代码需要被组织为模块或者命名空间这样的单位。

模块

默认情况下,F# 将所有代码都放置在匿名模块(anonymous module)中。模块的命名为首字母大写的源文件名。因此,如果在文件file1.fs中有一个名为value1的值,那么可以通过File1.value1来引用它。

创建模块

 

代码中可以在源文件开始处使用module关键字来明确指定模块名。在module定义之后的所有值、函数或类型定义都属于该模块。如:

module Alpha

//在模块外部使用Alpha.x来引用下面的值:

let x = 1

模块嵌套

 

单个源文件中可以包含嵌套的模块。被嵌套的模块应该使用module关键字后跟一个等号来声明。嵌套模块必须比上层模块缩进对齐。如:

module Utilities

module ConversionUtils =

    // Utilities.ConversionUtils.intToString

    let intToString (x : int) = x.ToString()

    module ConvertBase =

        // Utilities.ConversionUtils.ConvertBase.convertToHex

        let convertToHex x = sprintf "%x" x

        // Utilities.ConversionUtils.ConvertBase.convertToOct

        let convertToOct x = sprintf "%o" x

module DataTypes =

        // Utilities.DataTypes.Point

        type Point = Point of float * float * float

命名空间

 

除了模块之外还可以使用命名空间来组织代码。命名空间和模块的唯一区别在于,命名空间中不能包含值,只能包含类型定义。另外,命名空间不能像模块一样嵌套,而只需要直接在同一个源文件中定义多个命名空间。下面的例子在两个命名空间中定义了一些类型:

namespace PlayingCards

// PlayingCard.Suit

type Suit =

    | Spade

    | Club

    | Diamond

    | Heart

// PlayingCards.PlayingCard

type PlayingCard =

    | Ace of Suit

    | King of Suit

    | Queen of Suit

    | Jack of Suit

    | ValueCard of int * Suit

namespace PlayingCards.Poker

// PlayingCards.Poker.PokerPlayer

type PokerPlayer = { Name : string; Money : int; Position : int }

在F#中部推荐同时使用命名空间和模块。模块主要用于快速进行原型定义和快速浏览解决方案。而命名空间主要用于使用面向对象设计的较大型的项目。

程序的启动

 

F#中程序是从最后一个源文件的顶部开始执行的。假如现在有包含下面代码的单文件项目:

// Program.fs

let numbers = [1 .. 10]

let square x = x * x

let squaredNumbers = List.map square numbers

printfn "SquaredNumbers = %A" squaredNumbers

open System

printfn "(press any key to continue)"

Console.ReadKey(true)

在VS 2010中再添加一个新的空F#源文件。这时,最后一个文件是新添加的空文件,因此如果此时按F5运行程序时,不会执行任何代码。

这一点需要特别注意,否则程序的执行可能会对初学者造成混乱。可以说F#这个设计真的是非常之奇怪,不但没有任何好处,还会让人非常之不爽。。。。。。。。。

更正式的做法是使用[<EntryPoint>]属性来定义一个main函数。main函数需要满足以下几个要求:

  • 必须是项目中被编译的最后一个源文件中定义的最后一个函数。
  • 接受单个类型为string array的参数,这是操作系统传递给程序的参数。
  • 返回一个整数,作为程序的退出码。

如果要明确指定main函数,那么上面那个程序应该改写为:

// Program.fs

open System

[<EntryPoint>]

let main (args : string[]) =

    let numbers = [1 .. 10]

    let square x = x * x

    let squaredNumbers = List.map square numbers

    printfn "SquaredNumbers = %A" squaredNumbers

    printfn "(press any key to continue)")

    Console.ReadKey(True) |> ignore

    // Return 0    

    0

posted @ 2010-06-20 09:23 NetRoc 阅读(654) | 评论 (0)编辑 收藏

2010年5月24日

F#学习笔记之四 — 核心类型

     摘要: NetRoc http://www.DbgTech.net 除了基本类型之外,F#类库还包括一些核心类型用来操作数据。 类型标记 类型名 ...  阅读全文

posted @ 2010-05-24 12:45 NetRoc 阅读(926) | 评论 (0)编辑 收藏

2010年5月17日

F#学习笔记之三 — 函数

NetRoc

http://www.DbgTech.net/

  1. 函数的定义

    函数的定义也是采用let语句,所不同的是函数名后面可以跟参数列表。语法如下:

    let FunctionName Param1 Param2 = FunctionBody

    和其他很多语言不同,F#中没有return关键字,每个函数的返回值就是函数中最后一个表达式的值。

    函数的调用方式为函数名+空格+参数1+空格+参数2……,如:

    > let add x y = x + y;;

    val add : int -> int -> int

     

    > add 1 2;;

    Val it : 3

    在FSI中的输出int -> int -> int表示函数接受两个int类型的参数,返回一个int类型的参数。

  2. 类型推理

    F#是静态类型的, 但是在编写程序的时候并不一定要求指定函数的所有参数的类型,因为编译器会根据使用情况来自动指定。这就是类型推理(Type Inference)。但是类型推理并不是动态类型,虽然编程时未指定类型,但是所有参数都是有类型的,并且编译期会进行类型检查。这一点我到现在都还很不适应,总觉得写起来有些怪异的感觉,但是可以想象这是因为F#的函数式设计的原因。

    例如,对上面那个函数传递float类型的参数是会报错的:

    > add 1.0 2.0;;

    add 1.0 2.0;;

    ----^^^^

    stdin(3,5): error FS0001: This expression has type float but is here used with type int.

    这是由于函数中没有更多信息能够表明参数的类型,而+操作符能用于几种类型,所以编译器只是简单的将它默认为int类型的加法。但是如果代码片断中还有其他语句能够提供一些关于类型的信息给编译器,那么它就能识别出来编程者正确的意图:

    > // Type inference in action

    let mult x y = x * y

    let result = mult 4.0 5.5;;

    val mult : float -> float -> float

    val result : float

    双分号";;"是FSI的代码输入终结符。上面这个例子在定义了mult函数之后紧接着使用它来计算两个float类型数的值,因此编译器推断出正确的类型应该是float。这些特性到目前为止我觉得有些自作聪明了,编译器引入这种所谓"智能"的特性常常在实际应用中是帮倒忙的,例如恶心的ActionScript就是如此。希望随着后面的学习能够改变我的看法吧,呵呵。

    除了自动的类型推理之外,也可以自己指定参数的类型。方法是在参数名后加上一个冒号和类型声明,并且将整个参数声明用园括号括起来。例如:

    > let add (x : float) y = x + y;;

    val add : float -> float -> float

    可以看到编译器将y的类型也推断成了float,这是因为只有重载版本的+才能接受两个不同类型的值。

    总的来说,传统的C/C++、以及动态类型的PHP、LUA等语言的程序员肯定会非常不适应F#这种"半动态类型"的设计。为什么做成这样暂时我还没有清晰的认识。

  3. 普通函数(Generic Functions)

    不知道叫成"普通函数"是否合适,呵呵。实际上想表达的并不是"普通"这个意思。

    有时候在代码中没有任何信息能够让编译器来推断参数的类型,这时编译器就把它作为"普通"参数。"普通"参数是没有类型的,它能够接受任何类型的数据,如整数、字符串或者浮点数。在FSI中的输出类似这样:

    > let ident x = x;;

    val ident : 'a -> 'a

    > ident "a string";;

    val it : string = "a string"

    > ident 1234L;;

    val it : int64 = 1234L

    "普通"参数的类型名字可以是以单引号开头后跟任意合法标识符的形式。但是一般来说都是用"'a"。这样就可以明确的声明一个参数是普通类型的。如:

    > let ident2 (x : 'a) = x;;

    val ident2 : 'a -> 'a

    > let ident (x: 'abc) = x;;

    val ident : 'abc -> 'abc

    ident和ident2函数实质上是一样的。

  4. 作用域

    F#中定义的值的作用域和其他语言类似。默认来说,值的作用域是在模块内部,即在它的定义之后的任何位置都可以使用。函数中定义的值的作用域在函数内部,函数中可以使用函数内部定义的值、以及在函数外部并且位于函数之前定义的值。

    F#中允许嵌套函数,在内层函数中可以使用外层定义的所有值,但是在外层不能访问内层函数定义的值。当内层函数中定义的值名字和位于外层的值名字相同时,所访问的始终是内层定义的那个值。

    F#的作用域是很常规的设计,没有什么特殊的东西需要注意的。

  5. 控制流

    F#中可以使用if … then …的形式来产生程序分支。这条语句和其他语言中的类似语句作用一样,没什么好说的。需要注意的是F#采用代码对齐的方式来管理代码块,所以使用if语句时一定要注意代码的对齐。

    这种方式其实让代码看起来变得很不舒服,没有明显的代码块分隔符的时候,复杂语句很容易让人头晕眼花,比如下面这个:

    let isWeekend day =

        if day = "Sunday" then

            true

        else

            if day = "Saturday" then

                true

            else

                false

    在多个if语句嵌套的时候,可以使用下面这种形式来简化代码:

    let isWeekday day =

        if day = "Monday" then true

        elif day = "Tuesday" then true

        elif day = "Wednesday" then true

        elif day = "Thursday" then true

        elif day = "Friday" then true

        else false

    if语句的结果也是一个值,因此在if中的所有分支都必须返回相同类型的值。

    > // ERROR: Different types for if expression clauses

    let x =

        if 1 > 2 then

            42

        else

            "a string";;

                        else "a string";;

    ------------------^^^^^^^^^^^

    stdin(118,19): error FS0001: This expression has type string but is here used with type

    int. stopped due to error

    当if语句没有else的时候,返回的类型为unit。在F#中unit是一个特殊类型,用来表示"无值"类似于C/C++系列语言中的void。

posted @ 2010-05-17 16:07 NetRoc 阅读(642) | 评论 (0)编辑 收藏

2010年5月16日

F#学习笔记之二 — 数据类型

     摘要: NetRoc 2010.5.16 http://www.DbgTech.net/   F#是一个静态类型的语言,即所有类型都在编译时就确定。F#支持.NET库中的所有类型。 基本类型 数值类型。数值类型包括整数和浮点数。 使用let语句可以定义新的数值。可以带...  阅读全文

posted @ 2010-05-16 10:00 NetRoc 阅读(798) | 评论 (0)编辑 收藏

2010年5月15日

F#学习笔记之一 — 工具使用

NetRoc

  1. F#是依靠代码对齐来定义代码块的。这一点吸取了一些新派语言的特性,强制程序员采取可读性高的格式。
  2. VS 2010项目中按Ctrl+Alt+F可以打开F# Interactive窗口,可以直接在这个窗口内部输入和测试F# 语句。这是比较爽的一个功能,对我这种喜欢随写随测的Programmer来说是个福音。
  3. 打开F# Interactive窗口之后,按Alt+Enter,可以直接将代码编辑窗口中的选中部分输入进去。可以用来直接测试已经输入的代码片断。
  4. VS 2010中,F#代码的编译顺序非常重要。因为F#只能识别位于调用处之前定义,或者在之前编译过的文件中的类和函数。而VS 2010项目窗口中的F#源文件是按照从上到下的顺序来编译的,所以添加新源文件的时候要注意放置的位置。
  5. 可以执行fsc.exe来打开一个F# Interactive的命令行窗口。

posted @ 2010-05-15 18:34 NetRoc 阅读(750) | 评论 (0)编辑 收藏

2010年5月13日

摘录

        在这个世界上销量最高的游戏机游戏,如果现在让你猜的话,你一定猜不到,它是Wii Play——截至到2009年,它在全球销售了2671万套,这个数字还在不断攀升——而对于骨灰级玩家而言,这显得不可思议——它不过是根据Wii特色控制器手柄而设计的9款迷你小游戏而已!对,你会说它面对的不是真正的游戏玩家,可问题是,它确实是一款游戏套装!游戏中包括的9款迷你小游戏分别为:打靶、寻找Mii、乒乓球、印章、曲棍球、台球、钓鱼、赛牛和坦克大战。通过这些游戏玩家可以对Wii控制器手柄的各项功能逐步了解、上手,十分适合初次接触Wii的玩家。WiiPlay广泛利用到Mii人物和Wiimote,因为大多数游戏需要完成正确的手势,指向屏幕上的Mii人物和道具或者是击中一个乒乓球。在FindMii这个小游戏中你需要寻找最快移动的Mii或者一对一样的Miis。Fishing(钓鱼)则充分利用了Wiimote的特性。WiiPlay还捆绑了一个免费的Wiimote,这使得它非常超值。可能WiiPlay看起来不是很有深度,但它是我们这个星球的销量之王!它比目前澳大利亚人口总数(2200万人)还要多,并且它的销量已经被载入世界吉尼斯纪录大全。

  它不是你最爱的《辐射》系列,它也不是《实况足球》,它也不是伟大的“机战”系列作品,它不是《三国志》,它不是基于D&D规则的伟大的RPG,而是WiiPlay这样轻松的小游戏。这给人以很多的反思,比如我们所知道的当下风靡全国的种菜游戏令人吃惊的流行。

  Wii的另一套游戏外设及与之相配的软件Wii Fit则卖出了2256万套,它是一款使用Wii平衡板进行游玩的独特游戏。在游戏中,玩家可以利用身体的左右摇摆来玩呼啦圈,扭动身体来锻炼自己的头球技巧,WiiFit所能做到的还不止这些,你甚至可以用它来练习瑜伽、测量体重及重心,锻炼肌肉、练习瑜伽,以及锻炼平衡力和进行有氧运动。任天堂为了让这款游戏获得更多人的关注和信赖,还在其全球的官方网页Wii.com公开了专为WiiFit而设的卡路里计算器,让玩家可以在进行游戏中的项目时,顺便计算一下自己消耗了多少卡路里。计算卡路里的方式主要取决于玩家选择的难度,难度越高,消耗的卡路里也越多。其实,这个卡路里计算器只能在网上进行计算,并不能与游戏同步。但不止有多少游戏玩家的女友或者家庭主妇会……我们不说爱上它,起码她们会买它!

posted @ 2010-05-13 20:07 NetRoc 阅读(412) | 评论 (0)编辑 收藏

仅列出标题  下一页

导航

统计

常用链接

留言簿(7)

随笔档案(99)

文章分类(35)

文章档案(32)

Friends

Mirror

搜索

最新评论

阅读排行榜

评论排行榜