Tuesday, August 13, 2013

网络编程专题总结(重要)

网络编程专题总结(重要)   

一:确认网络环境3G/WIFI 二:使用NSConnection下载数据 三:使用NSXMLParser解析xml文件
一:确认网络环境3G/WIFI

    1. 添加源
文件和framework
    开发Web等网络应用程序的时候,需要确认网络环境,连接情况等信息。如果没有处理它们,是不会通过
Apple的审(我们的)查的。
    Apple 的 例程 Reachability 中介绍了取得/检测网络状态的方法。要在应用程序程序中使用Reachability,首先要完成如下两部:
    1.1. 添加源文件:
    在你的程序中
使用 Reachability 只须将该例程中的 Reachability.h 和 Reachability.m 拷贝到你的工程中。如下图:
    1.2.添加framework:
    将SystemConfiguration.framework 添加进工程。如下图:
    2. 网络状态
    Reachability.h中定义了三种网络状态:
    typedef enum {
        NotReachable = 0,            //无连接
        ReachableViaWiFi,            //使用3G/GPRS网络
        ReachableViaWWAN            //使用WiFi网络
    } NetworkStatus;
    因此可以这样检查网络状态:
    Reachability *r = [Reachability reachabilityWithHostName:@“
www.apple.com”];
    switch ([r currentReachabilityStatus]) {
            case NotReachable:
                    // 没有网络连接
                    break;
            case ReachableViaWWAN:
                    // 使用3G网络
                    break;
            case ReachableViaWiFi:
                    // 使用WiFi网络
                    break;
    }
    3.检查当前网络环境
    程序启动时,如果想检测可用的网络环境,可以像这样
    // 是否wifi
    + (BOOL) IsEnableWIFI {
        return ([[Reachability reachabilityForLocalWiFi] currentReachabilityStatus] != NotReachable);
    }
    // 是否3G
    + (BOOL) IsEnable3G {
        return ([[Reachability reachabilityForInternetConnection] currentReachabilityStatus] != NotReachable);
    }
    例子:
    - (void)viewWillAppear:(BOOL)animated {    
    if (([Reachability reachabilityForInternetConnection].currentReachabilityStatus == NotReachable) && 
            ([Reachability reachabilityForLocalWiFi].currentReachabilityStatus == NotReachable)) {
            self.navigationItem.hidesBackButton = YES;
            [self.navigationItem setLeftBarButtonItem:nil animated:NO];
        }
    }
    4. 链接状态的实时
通知
    网络连接状态的实时检查,通知在网络应用中也是十分必要的。接续状态发生变化时,需要及时地通知用户:
    Reachability 1.5版本
    // My.AppDelegate.h
    #import "Reachability.h"
    @interface MyAppDelegate : NSObject <UIApplicationDelegate> {
        NetworkStatus remoteHostStatus;
    }
    @property NetworkStatus remoteHostStatus;
    @end
    // My.AppDelegate.m
    #import "MyAppDelegate.h"
    @implementation MyAppDelegate
    @synthesize remoteHostStatus;
    // 更新网络状态
    - (void)updateStatus {
        self.remoteHostStatus = [[Reachability sharedReachability] remoteHostStatus];
    }
    // 通知网络状态
    - (void)reachabilityChanged:(NSNotification *)note {
        [self updateStatus];
        if (self.remoteHostStatus == NotReachable) {
            UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"AppName", nil)
                         message:NSLocalizedString (@"NotReachable", nil)
                        delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];
            [alert show];
            [alert release];
        }
    }
    // 程序启动器,启动网络监视
    - (void)applicationDidFinishLaunching:(UIApplication *)application {
        // 设置网络检测的站点
        [[Reachability sharedReachability] setHostName:@"www.apple.com"];
        [[Reachability sharedReachability] setNetworkStatusNotificationsEnabled:YES];
        // 设置网络状态变化时的通知函数
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reachabilityChanged:)
                                                 name:@"kNetworkReachabilityChangedNotification" object:nil];
        [self updateStatus];
    }
    - (void)dealloc {
        // 删除通知对象
        [[NSNotificationCenter defaultCenter] removeObserver:self];
        [window release];
        [super dealloc];
    } 
    
    Reachability 2.0版本
    // MyAppDelegate.h
    @class Reachability;

        @interface MyAppDelegate : NSObject <UIApplicationDelegate> {
            Reachability  *hostReach;
        }

    @end

    // MyAppDelegate.m
    - (void)reachabilityChanged:(NSNotification *)note {
        Reachability* curReach = [note object];
        NSParameterAssert([curReach isKindOfClass: [Reachability class]]);
        NetworkStatus status = [curReach currentReachabilityStatus];
    
        if (status == NotReachable) {
            UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"AppName""
                              message:@"NotReachable"
                              delegate:nil
                              cancelButtonTitle:@"YES" otherButtonTitles:nil];
                              [alert show];
                              [alert release];
        }
    }
                              
    - (void)applicationDidFinishLaunching:(UIApplication *)application {
        // ...
                  
        // 监测网络情况
        [[NSNotificationCenter defaultCenter] addObserver:self
                              selector:@selector(reachabilityChanged:)
                              name: kReachabilityChangedNotification
                              object: nil];
        hostReach = [[Reachability reachabilityWithHostName:@"www.google.com"] retain];
        hostReach startNotifer];
        // ...
    }
二:使用NSConnection下载数据
    1.创建NSConnection对象,设置委托对象
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[self urlString]]];
    [NSURLConnection connectionWithRequest:request delegate:self];
    2. NSURLConnection delegate委托方法
        - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;  
        - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;  
        - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;  
        - (void)connectionDidFinishLoading:(NSURLConnection *)connection;  

    3. 实现委托方法
    - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
        [self.receivedData setLength:0]; //通常在这里先清空接受数据的缓存
    }
    - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
        [self.receivedData appendData:data]; //可能多次收到数据,把新的数据添加在现有数据最后
    }
    - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
        // 
错误处理
    }
    - (void)connectionDidFinishLoading:(NSURLConnection *)connection {
        // disconnect
        [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;   
        NSString *returnString = [[NSString alloc] initWithData:self.receivedData encoding:NSUTF8StringEncoding];
        NSLog(returnString);
        [self urlLoaded:[self urlString] data:self.receivedData];
        firstTimeDownloaded = YES;
    }
三:使用NSXMLParser解析xml文件
  1. 设置委托对象,开始解析
    NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];   //或者也可以使用initWithContentsOfURL直接下载文件,但是有一个原因不这么做:
    // It's also possible to have NSXMLParser download the data, by passing it a URL, but this is not desirable
    // because it gives less control over the network, particularly in responding to connection errors.
    [parser setDelegate:self];
    [parser parse];

    2. 常用的委托方法
    - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName 
                                namespaceURI:(NSString *)namespaceURI
                                qualifiedName:(NSString *)qName 
                                attributes:(NSDictionary *)attributeDict;
    - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName 
                                namespaceURI:(NSString *)namespaceURI 
                                qualifiedName:(NSString *)qName;
    - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string;
    - (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError;

    static NSString *feedURLString = @"http://www.yifeiyang.net/test/test.xml";

    3.  应用举例
    - (void)parseXMLFileAtURL:(NSURL *)URL parseError:(NSError **)error
    {
        NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:URL];
        [parser setDelegate:self];
        [parser setShouldProcessNamespaces:NO];
        [parser setShouldReportNamespacePrefixes:NO];
        [parser setShouldResolveExternalEntities:NO];
        [parser parse];
        NSError *parseError = [parser parserError];
        if (parseError && error) {
            *error = parseError;
        }
        [parser release];
    }

    - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI 
                                        qualifiedName:(NSString*)qName attributes:(NSDictionary *)attributeDict{
        // 元素开始句柄
        if (qName) {
            elementName = qName;
        }
        if ([elementName isEqualToString:@"user"]) {
            // 输出属性值
            NSLog(@"Name is %@ , Age is %@", [attributeDict objectForKey:@"name"], [attributeDict objectForKey:@"age"]);
        }
    }

    - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI 
                                        qualifiedName:(NSString *)qName
    {
        // 元素终了句柄
        if (qName) {
               elementName = qName;
        }
    }
    - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
    {
        // 取得元素的
text
    }
    NSError *parseError = nil;
    [self parseXMLFileAtURL:[NSURL URLWithString:feedURLString] parseError:&parseError];
    

Tuesday, June 11, 2013

Guide to building FFMpeg for Windows Mobile / Win CE

Re: Guide to building FFMpeg for Windows Mobile / Win CE

Postby ceebmoj on Tue Mar 31, 2009 9:16 am
Hi all,

I after playing round with Crags guide I thought I would post what I have found. No small amount of hair pulling from me to

Prerequisites

1. Before proceeding, you must have the Msys + MingW development environment set up. I can highly recommend thishttp://ffmpeg.arrozcru.org/ guide as a starting point. Follow this guide through until you have an environment which is sufficient for building FFMpeg on Windows.

2. Get the CeGCC cross compiler from thehttp://sourceforge.net/project/showfiles.php?group_id=173455&package_id=198682 . The version you should get is http://downloads.sourceforge.net/cegcc/cegcc-mingw32ce-0.50-cygwin.tar.gz?modtime=1188210321&big_mirror=0%20cegcc-mingw32ce-0.50-cygwin.tar.gz.

3. Unzip cegcc-cegcc-0.50-cygwin.tar.gz into your msys/mingw directory. Assuming you followed the guid in step 1, that will be
"c:\msys\mingw"

4. Rename the winsock 2 library found in
"/mingw/opt/mingw32ce/arm-wince-mingw32ce/lib"

from libws2.a to libws2_32.a. Assuming you followed the guide in step 1 that will be
"C:\msys\mingw\opt\mingw32ce\arm-wince-mingw32ce\lib"

5. Modify the file errno.h found in
"/mingw/opt/mingw32ce/arm-wince-mingw32ce/include/errno.h"

Remove lines 11-14:

#ifdef __COREDLL__
# include_next <errno.h>
#else /* __COREDLL__ */

and lines 106-107:

#endif /* Not __COREDLL__ */

6. Note that this is only an issue if you are ''not'' using CeGCC to build your Windows CE / Mobile application. There is an issue with the alignment of structures that contain 8-byte variables in CeGCC (see this http://www.mail-archive.com/cegcc-devel@lists.sourceforge.net/msg00738.html discussion for more info). The solution for this problem involves modifying the stdint.h header file that ships with the release of CeGCC.

/mingw/opt/mingw32ce/arm-wince-mingw32ce/include/stdint.h

If the guide in step 1 was followed that will be
C:\msys\mingw\opt\mingw32ce\arm-wince-mingw32ce\include\stdint.h

Modify the typedefs of the 64-bit integer variables from:

typedef long long int64_t;
typedef unsigned long long uint64_t;

to:

typedef long long int64_t
#if defined(__GNUC__)
__attribute((aligned(8)))
#endif
;

typedef unsigned long long uint64_t
#if defined(__GNUC__)
__attribute((aligned(8)))
#endif
;

7. Download http://www.gnuarm.com/bu-2.16.1_gcc-4.1.0-c-c++_nl-1.14.0_gi-6.4.exe binutils-2.16.1, gcc-4.1.0-c-c++, newlib-1.14.0, insight-6.4, setup.exe [25.3MB] from http://www.gnuarm.com.
8. Install the gnuArm tools to the default location.

Build Process
1. Check out the latest version of the FFMpeg source from SVN. The rest of this guide will assume you checked out to c:\projects\ffmpeg.
2. Open a MSYS command window and navigate to the directory you checked FFMpeg out to:

cd /c/projects/ffmpeg

3. Add the path to the CeGCC cross compiler's bin directory to your PATH environment variable:

PATH=$PATH:/mingw/opt/mingw32ce/bin

4. Add the path to the gnuArm tools bin directory to your PATH environment variable:

PATH=$PATH:"/C/Program Files/GNUARM/bin"

5. Configure the build by executing the following command:

./configure --enable-mingwce --cross-compile --cross-prefix=arm-wince-mingw32ce- --arch=arm --disable-static --enable-shared --disable-parsers --enable-memalign-hack --disable-ffmpeg --disable-ffplay --disable-debug
note from metalkin) It seems that ffmpeg doesn't support '--enable-mingwce' and '--cross-compile'options anymore, '--enable-cross-compile' could replace '--cross-compile' based on current ffmpeg revision.
During build, dsputil_arm_s.S makes several errors "' junk at end of line, first unrecognized character is `p'". I guess an arm assember has to be designated to compile it.
Due to the reasons, this option could be a bit out of date, I think.

6. Remove any old intermediate files

make clean

7. Start the build by executing make:

make

8. Once the build is finished.

make install

9. The lib and header files will now have been copied over to the directories below.

/usr/local/lib
/usr/local/include/ffmpeg

Which if you followed the guide in the prerequisites will be located in:

C:\msys\local\lib
C:\msys\local\include\ffmpeg

Have fun

ceebmoj


编译环境:msys 3.15 ,同时有安装mingw和Cygwin(具体版本我自己都已经搞不大清楚了)
ffmpeg版本:0.5.2,下载地址:http://ffmpeg.org/releases/ffmpeg-0.5.2.tar.bz2

先是一些准备工作,把之前的一些文档找出来,

ffmpeg编译手册:编译Windows Mobile/Windows CE版的ffmpeg

此文主要摘录自:http://ffmpeg.arrozcru.org/forum/viewtopic.php?f=8&t=780

Guide to building FFMpeg for Windows Mobile / Win CE

First Guide:
http://www.craigshaw.com/2008/04 ... EWindowsMobile.aspx
但是据很多人讲,依据这篇文章根本无法真正编译出Windows Mobile版的ffmpeg。更不幸的是,似乎是GFW把这篇这么好的文章给“墙”住了,所以似乎更是让你一点线索都没有。


不过还好,有人根据那篇Guide,再做了一些补充,据说可以编译出来,以下是其说明和补充的步骤:

I after playing round with Crags guide I thought I would post what I have found. No small amount of hair pulling from me to

Prerequisites 

1. Before proceeding, you must have the Msys + MingW development environment set up. I can highly recommend thishttp://ffmpeg.arrozcru.org/ guide as a starting point. Follow this guide through until you have an environment which is sufficient for building FFMpeg on Windows.

2. Get the CeGCC cross compiler from the http://sourceforge.net/project/s ... p;package_id=198682 . The version you should get is http://downloads.sourceforge.net ... 0.50-cygwin.tar.gz.

3. Unzip cegcc-cegcc-0.50-cygwin.tar.gz into your msys/mingw directory. Assuming you followed the guid in step 1, that will be 
"c:\msys\mingw"

4. Rename the winsock 2 library found in 
"/mingw/opt/mingw32ce/arm-wince-mingw32ce/lib"

from libws2.a to libws2_32.a. Assuming you followed the guide in step 1 that will be
"C:\msys\mingw\opt\mingw32ce\arm-wince-mingw32ce\lib"

5. Modify the file errno.h found in
"/mingw/opt/mingw32ce/arm-wince-mingw32ce/include/errno.h"

Remove lines 11-14:

#ifdef __COREDLL__
# include_next <errno.h>
#else /* __COREDLL__ */

and lines 106-107:

#endif /* Not __COREDLL__ */

6. Note that this is only an issue if you are ''not'' using CeGCC to build your Windows CE / Mobile application. There is an issue with the alignment of structures that contain 8-byte variables in CeGCC (see this http://www.mail-archive.com/cegc ... e.net/msg00738.html discussion for more info). The solution for this problem involves modifying the stdint.h header file that ships with the release of CeGCC. 

/mingw/opt/mingw32ce/arm-wince-mingw32ce/include/stdint.h

If the guide in step 1 was followed that will be 
C:\msys\mingw\opt\mingw32ce\arm-wince-mingw32ce\include\stdint.h

Modify the typedefs of the 64-bit integer variables from:

typedef long long int64_t;
typedef unsigned long long uint64_t;

to:

typedef long long int64_t
#if defined(__GNUC__)
__attribute((aligned(8)))
#endif
;

typedef unsigned long long uint64_t
#if defined(__GNUC__)
__attribute((aligned(8)))
#endif
;

7. Download http://www.gnuarm.com/bu-2.16.1_ ... l-1.14.0_gi-6.4.exe binutils-2.16.1, gcc-4.1.0-c-c++, newlib-1.14.0, insight-6.4, setup.exe [25.3MB] from http://www.gnuarm.com.
8. Install the gnuArm tools to the default location. 

Build Process
1. Check out the latest version of the FFMpeg source from SVN. The rest of this guide will assume you checked out to c:\projects\ffmpeg.
2. Open a MSYS command window and navigate to the directory you checked FFMpeg out to:

cd /c/projects/ffmpeg

3. Add the path to the CeGCC cross compiler's bin directory to your PATH environment variable:

PATH=$PATH:/mingw/opt/mingw32ce/bin

4. Add the path to the gnuArm tools bin directory to your PATH environment variable:

PATH=$PATH:"/C/Program Files/GNUARM/bin"

5. Configure the build by executing the following command:

./configure --enable-mingwce --cross-compile --cross-prefix=arm-wince-mingw32ce- --arch=arm --disable-static --enable-shared --disable-parsers --enable-memalign-hack --disable-ffmpeg --disable-ffplay --disable-debug

6. Remove any old intermediate files 

make clean

7. Start the build by executing make:

make

8. Once the build is finished.

make install

9. The lib and header files will now have been copied over to the directories below.

/usr/local/lib
/usr/local/include/ffmpeg

Which if you followed the guide in the prerequisites will be located in:

C:\msys\local\lib
C:\msys\local\include\ffmpeg

Wednesday, March 20, 2013

2011年最新最全的Cydia的源地址


資源的名稱資源的地址網站裡面的內容
開發解鎖http://apt9.yellowsn0w.com/黃雪解鎖
iFonetechttp://app.ifonetec.com/cydia/Cylay / MiVTones
威鋒http://app.weiphone.com/cydia/通知/俄羅斯運營商包/黑客
iPhoneCat隊http://apple.bloks.cat/repo/加泰羅尼亞語語言支持
iPhone伊斯蘭教http://apps.iphoneislam.com/阿拉伯語言支持
4pp13隊http://apt.123Locker.comNES / GBA ROM包
BigBoss與地球的iPhonehttp://apt.bigboss.us.com/repofiles/cydia/主題/調整/黑客/應用程序/壁紙
Darvens資源庫http://apt.guardiansofchaos.com/主題/壁紙/應用程序/鈴聲
Hackers.nlhttp://apt.hackers.nl/補丁/黑客/ Zuijlen.eu的回購
IngiliZanahtarihttp://apt.ingilizanahtari.com/土耳其語言支持
ModMyiFonehttp://apt.modmyi.com主題/壁紙/應用程序/鈴聲
Telesphoreo橘柚http://apt.saurik.com/蘋果蠹來源/ unix中utils的/ cycorder的的/黑客
Steffwizhttp://apt.steffwiz.com/dists/steffwiz/main/binary-iphoneos-arm/所有的源/黑客/ MobileInstallation的2.2補丁
iFone1挪威http://c.ifon1.no/補丁/黑客
iModZone蘋果蠹回購http://c.imodzone.net/RockBand /黑客
SOSiPhonehttp://cy.sosiphone.com/法國語言支持
Hackulohttp://cydia.hackulo.us/黑客:)
iClarified.comhttp://cydia.iclarified.com/卷升壓/ ILOG /自動更正ONOFF
iFoneguidehttp://cydia.ifoneguide.nl/荷蘭語言支持
iPhoneModhttp://cydia.iphonemod.com.br/巴西式的語言支持
Intellibornhttp://intelliborn.com/cydiaIntelliScreen
免費編碼器http://iphone.freecoder.org/apt/支持ICOSTA中國輸入
ispaziohttp://ispaziorepo.com/cydia/apt/背景/應用程序/遊戲/主題
iphonealhttp://mc2.iphoneall.org/印尼
技巧
SaladSofthttp://nickplee.com/cydiasource/和弦指南尼克的APP實驗
豐富的創作http://www.richcreations.com/iphone/apt/N / A
代碼種族滅絕http://repo.codegenocide.com/cydia/iSteamy(成人組)
安迪大壩http://repo.gafoogle.com/從Safari的installous投資促進機構的一個水龍頭安裝
Urbanfanaticshttp://urbanfanatics.com/cydia/ScuummVM
weho.ruhttp://weho.ru/iphone/通知/俄羅斯運營商包/黑客
iacceshttp://www.iacces.com/apt/的中國鍵盤和黑客
iphone.org.hkhttp://www.iphone.org.hk/apt/的MobileInstallation補丁/ HK設置/ lighttpd的
iPhone視頻錄像機http://www.iphonevideorecorder.comiPhone視頻錄像機
xsellize.comhttp://xsellize.com/cydia/應用程序/工具/補丁
zodttd.comhttp://zodttd.com/repo/cydia/仿真器/ Quake4iPhone /末日VLC4Iphone
hackers.nlhttp://in apr.hackers.nl主題/應用程序/鈴聲
免費編碼器http://iphone.freecoder.org/apt/iCosta後
http://cake.mapleidea.comN / A
Tmgrepoftp://tmgrepo.docspages.com:9866058/N / A
iPhone的storage.dehttp://apt.iphone-storage.de/N / A
尼克拉斯·施羅德http://apt.paperclipsandscrambledeggs.com/N / A
Zanekills“源http://apt.zanekills.com/N / A
CZ&SKhttp://csid。tym.cs /回購/N / A
馬爾辛拉伯(波蘭)http://cydia.i-apps.pl/在波蘭的應用程序和黑客
Comcute和壁虎http://gecko.pri.ee/cydia/N / A
HackmyiPhonehttp://hackmyiphone.info/cydia/N / A
“近期行動計劃”回購http://iparepo.comN / A
哈克和Dev.orghttp://iphone.hackndev.org/apt/iCommander,一些修補程序
iPhonehe.com(希伯來文)http://iphonehe.com/iphoneN / A
iPhonestuffhttp://iphonestuff.ru/N / A
iPuhelinhttp://ipuhelin.com/cydia/應用程序/黑客
iPhone補丁(保加利亞)http://mspasov.com/N / A
SMXYhttp://repo.smxy.org/cydia/apt/N / A
UCWEBhttp://wap.ucweb.com/iPhone/cydiaUCWEB

Friday, March 15, 2013

使用Lua Function表示Lambda calculus


很多程序語言所帶給你的“完美”的感覺都來自於數學抽象之美。
在Lua中,function被描述成“具有真正的詞法範圍的一類值”(first-class values​​ with proper lexical scoping)。
所謂的“一類值”,應該滿足以下條件:
  • 可以被保存到變量和數據結構中
  • 可以被當作子程序的參數和返回值
  • 可以在運行期創建
  • 具有內在標識,不依賴於任何給定的名稱
大多數語言的基本數據類型,比如int,就屬於“一類值”。很多語言中的函數實​​現,只能滿足其中一些條件。比如在C中可以將函數指針保存到變量中,可以將函數指針當作參數和返回值。這樣的函數實現一般不會被算作“一類值"。
在Lua中,所有的值都是“一類”值,包括function本身。函數可以被保存到任何的變量或者table中,可以被當作參數和返回值使用,所有的函數(更準確的說應該是closure)都是運行期被創建的,函數本身並沒有名字,名字只是對函數的引用而已。作為“一類值”的function更為抽象,可以用來表示很多的"Functional Programming"的概念。比如“高階函數”(Higher-order functions),“匿名函數”(Anonymous functions")。這些功能在很多語言中都是通過特殊的語法來支持的,而在lua中則不需要。
而所謂的“真正詞法範圍”,則是說Lua function可以訪問外圍函數中的局部變量。這是通過lua的closure來實現的。
“具有真正的詞法範圍的一類值”使得Lua function可以用來表示" Lambda calculus "。Lambda calculus是Functional Programming的數學基礎。他使用抽象的function和一套簡單的規則來構建一個完整的等價於"Turing Machine"的計算模型。使用Lua function來表示Lambda calculus可以讓你從另一個更真實的角度去理解Lambda calculus的語義,同時也可以更深入的體會Lua function功能的強大。並且對於程序員來說,這本身也是一個非常有趣的嘗試。
Lambda calculus是一個操作lambda expression的系統。Lambda expression由variable,function abstraction和function application組成。我們可以使用一個Lua function的定義來代表一個function abstraction。這個function接受一個參數,也就是variable,並返回一個function。而對這個function的調用,就是function application。這樣,我們就有了lambda calculus基本規則對應的lua實現。Lambda calculus可以通過如此簡單的基本規則構建出各種高層語義,比如數據類型,算數和邏輯運算,循環和遞歸等等。這也就是說,理論上我們可以僅僅使用lua function,而不需要任何其他的語言功能,來進行任何的計算。我想這也就是"functional programming"的極限了吧。
我們首先來看一些最簡單的function。

Identity

λx.x
單位函數直接返回應用的參數。對應的lua function為:
  1. function identity(x)  
  2.     return x;  
  3. end  
將一個identity應用到identity,結果還應該是identity。
λx.x λx.x=>λx.x
  1. print(identity(identity) == identity)  
Lua中的結果應該是true

Self Application Function

λs.(ss)
自應用函數將參數應用到參數本身。對應的lua function為:
  1. function self_apply(s)  
  2.     return s(s);  
  3. end  
將自應用函數應用到identity,最終會得到identity:
λs.(ss) λx.x => λx.x λx.x => λx.x
  1. print(self_apply(identity) == identity);  
將自應用函數應用到自身,會造成估值不能結束:
λs.(ss) λs.(ss) => λs.(ss) λs.(ss) =>...
同樣,對於lua調用
  1. self_apply(self_apply)  
也會一直無限循環下去。由於self_apply函數中的s(s)是一個tail call,所以這個無限循環不會造成調用棧溢出。

Function Application Function

λf.λa.(fa)
函數應用函數將參數f應用到參數a上。對應的lua function為:
  1. function apply(f)  
  2.     return (function(a)  
  3.         return f(a);  
  4.     end);  
  5. end  
這個lua function與對應的lambda expression的語義完全一致:最外層函數接受一個參數f,函數體為λa.(fa),也就是一個參數為a的函數。而這個內層函數的函數體為(fa),也就是將f應用到a。
如果將此函數應用到identity:
λf.λa.(fa) λx.x => λa.(λx.x a)
會得到一個參數為a的函數。而對於lua function也是如此:
  1. apply(identity)  
會返回一個帶有identity upvalue的函數對象。
如果將此函數連續應用到identity:
λf.λa.(fa) λx.x λx.x =>λa.(λx.xa) λx.x => λx.x λx.x => λx.x
效果就和將identity應用到自身是一樣的。
對應的lua調用:
  1. print(apply(identity)(identity) == identity)  
應該返回true。
接下來,我們開始構建一些基礎的function,並在這些基礎上構建更高層的語義。

Boolean Values

在lambda calculas中,我們可以通過函數來表示boolean values​​。
  • TRUE可以表示成λf.λs.f,代表選擇兩個參數中的第一個。
  • FALSE可以表示成λf.λs.s,代表選擇兩個參數中的第二個。
其對應的lua function為:
  1. function TRUE(f)  
  2.     return (function(s)  
  3.         return f;  
  4.     end);  
  5. end  
  6.   
  7. function FALSE(f)  
  8.     return (function(s)  
  9.         return s;  
  10.     end);  
  11. end  
我們可以測試一下這個lambda expression:
TRUE identity apply == λf.λs.f identity apply => λs.identity apply => identity
FALSE identity apply == λf.λs.s identity apply => λs.s apply => apply
同樣,對應的lua調用也成立:
  1. print(TRUE(identity)(apply) == identity) -- true  
  2. print(FALSE(identity)(apply) == apply) -- true  

Condition

根據Boolean value的定義,我們可以構造出條件判斷的lambda function:
λt.λf.λb.(btf)
這個表達式的語義是根據boolean值b,來選擇t或者f。如果b為TRUE,就選擇t,否則選擇f。
其對應的lua function為:
  1. function COND(t)  
  2.     return (function(f)  
  3.         return (function(b)  
  4.             return b(t)(f);  
  5.         end);  
  6.     end);  
  7. end  
我們可以通過將條件表達式應用到identity,apply和TRUE,來看一下結果:
λt.λf.λb.(btf) identity apply TRUE =>  
λf.λb(b identity f) apply TRUE =>  
λb(b identity apply) TRUE =>  
TRUE identity apply =>  
identity
同樣,對應的Lua調用也成立:
  1. print(COND(identity)(apply)(TRUE) == identity) -- true  
這等同於如下邏輯:
  1. if(true)  
  2.     return identity  
  3. else  
  4.     return apply  
至此,我們已經看到,僅僅使用lua function,就可以構造出基於if...else...的邏輯判斷語義。

NOT

λb.(COND FALSE TRUE b) == λb.(λt.λf.λb(btf) FALSE TRUE b) => λb(b FALSE TRUE)
這個表達式的語義是:當b為TRUE時,選擇FALSE,否則選擇TRUE。
對應的lua function:
  1. function NOT(b)  
  2.     return b(FALSE)(TRUE);  
  3. end  
  1. print(NOT(TRUE) == FALSE); -- true  
  2. print(NOT(NOT(TRUE)) == TRUE); --true  

AND

λx.λy.(COND y FALSE x) == λx.λy.(λt.λf.λb(btf) y FALSE x) => λx.λy.(xy FALSE)
這個表達式的語義是:當x為TRUE時,選擇y,否則選擇FALSE。
對應的lua function:
  1. function AND(x)  
  2.     return (function(y)  
  3.         return x(y)(FALSE);  
  4.     end);  
  5. end  
  1. print(AND(TRUE)(TRUE) == TRUE); -- true  
  2. print(AND(TRUE)(FALSE) == FALSE); -- true  
  3. print(AND(FALSE)(TRUE) == FALSE); -- true  
  4. print(AND(FALSE)(FALSE) == FALSE); -- true  

OR

λx.λy.(COND TRUE yx) == λx.λy.(λt.λf.λb(btf) TRUE yx) => λx.λy.(x TRUE y)
這個表達式的語義是:當x為TRUE時,選擇TRUE,否則選擇y。
對應的lua function:
  1. function OR(x)  
  2.     return (function(y)  
  3.         return x(TRUE)(y);  
  4.     end);  
  5. end  
  1. print(OR(TRUE)(TRUE) == TRUE); -- true  
  2. print(OR(TRUE)(FALSE) == TRUE); -- true  
  3. print(OR(FALSE)(TRUE) == TRUE); -- true  
  4. print(OR(FALSE)(FALSE) == FALSE); -- true  
至此,我們已經有了基本的邏輯運算符NOT,AND和OR。可以通過他們來組合出更複雜的boolean邏輯表達式。

Natural Numbers

使用lambda expression表示自然數,我們首先要定義0。我們將0定義為identity。
  1. zero = identity  
然後,定義一個succ函數,代表自然數n的下一個自然數:
λn.λb.(b FALSE n)
  1. function succ(n)  
  2.     return (function(b)  
  3.         return b(FALSE)(n);  
  4.     end);  
  5. end  
這樣我們就可以定義出自然數1~9:
  1. one = succ(zero);  
  2. two = succ(one);  
  3. three = succ(two);  
  4. four = succ(three);  
  5. five = succ(four);  
  6. six = succ(five);  
  7. seven = succ(six);  
  8. eight = succ(seven);  
  9. nine = succ(eight);  
每個自然數都使用一個函數來表示。
接著我們需要定義一個函數iszero來判斷一個自然數是否為0:
λn.(n TRUE)
然後我們定義一個函數iszero,用來判斷是否是0:
λn.(n TRUE)
  1. function iszero(n)  
  2.     return n(TRUE);  
  3. end  
  1. print(iszero(zero) == TRUE) -- true  
  2. print(iszero(one) == FALSE) -- true  
最後,我們還可以定義一個pred函數,用來獲得一個自然數的前一個自然數:
λn.(COND zero (n FALSE) (iszero n)) => λn.((iszero n) zero (n FALSE))
  1. function pred(n)  
  2.     return iszero(n)(zero)(n(FALSE));  
  3. end  
這裡麵包含了當n為0時的特殊處理。當n為0時,返回0。
  1. print(pred(one) == zero) -- true  
至此,我們有了基本的自然數的表示方法。接下來,我們將利用自然數來計數,進行一個簡單的循環。

Loop

在Functional Programming中,循環使用遞歸調用來進行。Lambda calculus的遞歸調用是通過將一個Y Conbinator函數引用到一個stepper函數來實現的。stepper函數代表了循環的每一次需要做的事情,而YConbinator函數則多次調用這個stepper函數,來表示循環。
Y Conbinator:
λf.(λx.(f (xx)) λx.(f (xx)))
  1. function recursive(f)  
  2.     return (function(x)  
  3.         return f(x(x))  
  4.     )(function(x)  
  5.         return f(x(x))  
  6.     end)  
  7. end  
stepper函數我們將它設計為傳入一個自然數然後遞減:
λs.λn.(COND zero (s (pred n) (iszero n))
  1. function stepper(next_step)  
  2.     return (function(n)  
  3.         return COND(zero)(next_step(pred(n))(iszero(n))  
  4.     end)  
  5. end  
當我們調用:
  1. recursive(stepper)(five)  
這個調用並沒有之循環5次,而是會無限遞歸下去,直到棧溢出。原因在於我們對於lambda expression的reduction採用的是normal order,而lua版本實際上等於applicative order reduction。Applicative order reduction在此情況下會無限遞歸。解決這個問題的辦法就是延遲一些字表達式的估值。我們可以將這兩個函數改寫為:
  1. function recursive(f)  
  2.     return (function(x)  
  3.         return f(function(dummy) return x(x) end)  
  4.     end)(function(x)  
  5.         return f(function(dummy) return x(x) end)  
  6.     end)  
  7. end  
  8.   
  9. function stepper(next_step)  
  10.     return (function(n)  
  11.         return COND(function(dummy)  
  12.             return zero  
  13.         end)(function(dummy)  
  14.             print("one step");  
  15.             return next_step(identity)(pred(n))  
  16.         end)(iszero(n))(identity)  
  17.     end)  
  18. end  
當我們再次調用:
  1. recursive(stepper)(five)  
我們會看到這個循環正確的執行了5次。
綜上所述,我們已經使用lua function作為lambda calculas的表示形式,從新構建了一個包含了高層語義的計算模型,從而也體會到了lua function高度抽象的能力。希望對大家學習lambda calculus和lua function有所幫助。