GNU Smalltalk

维基百科,自由的百科全书
跳转到导航 跳转到搜索
GNU Smalltalk
File:GNU Smalltalk logo.svg
编程范型面向对象, 脚本
语言家族Smalltalk
实作者Steve Byrne(直到1.1.5),
Paolo Bonzini(自从1.6)[1]
发行时间2003年1月12日,​23年前​(2003-01-12
当前版本
    Module:EditAtWikidata第29行Lua错误:attempt to index field 'wikibase' (a nil value)
    操作系统UnixLinux, Cygwin, Mac OS X/Darwin
    许可证GPL(针对虚拟机) + LGPL(针对类库和映像)
    文件扩展名.st
    网站https://www.gnu.org/software/smalltalk/
    影响语言
    ANSI Smalltalk

    GNU SmalltalkSmalltalk编程语言GNU计划实现。

    这个实现不同于其他Smalltalk环境,使用文本文件作为程序输入,并将其内容解释为Smalltalk代码[3]。在这种方式下,GNU Smalltalk表现得更像是一种解释器,而非传统Smalltalk方式下的一种环境[4]。GNU Smalltalk包括了对很多自由软件库的绑定,包括SQLitelibSDLcairogettextExpat英语Expat (library)[5]

    简单例子[编辑]

    下面的例子可工作在GNU Smalltalk 3.0和以后版本上。经典的Hello, World!例子:

    'Hello, World!' displayNl
    

    GNU Smalltalk声明了三个文件:stdinstdoutstderr,作为文件串流类(FileStream)的全局实例,并绑定了适合传递给C虚拟机的值。对象类(Object)定义了特有于GNU Smalltalk的四个方法:printprintNlstorestoreNl。它们对接收者做一次printOn:storeOn:Transcript对象。这个对象是文本搜集器类(TextCollector)的唯一实例,它通常将写操作委托给stdout

    一些基本的Smalltalk代码:

    "所有东西,包括一个文字,都是一个对象,所以如下可行:"
    -199 abs.                                               "199"
    'gst is cool' size.                                     "11"
    'Slick' indexOf: $c.                                    "4"
    'Nice Day Isn''t It?' asLowercase asSet asSortedCollection asString "' ''?acdeinsty'"
    

    两个"包围的是注释;$c是字符常量c;两个'包围的是字符串,字符串中的'''转义序列

    搜集[编辑]

    构造和使用一个数组

    a := #(1 'hi' 3.14e0 1 2 (4 5)).
    
    a at: 3.       "3.14"
    a reverse.     "((4 5) 2 1 3.14 'hi' 1)"
    a asSet        "Set(1 'hi' 3.14 2 (4 5))"
    

    构造和使用一个散列表,它是散列搜集类(HashedCollection)的子类即字典类(Dictionary)的实例:

    hash := Dictionary from: { 'water' -> 'wet'. 'fire' -> 'hot' }.
    hash at: 'fire'     "'hot'".
    
    hash keysAndValuesDo:
           [ :k :v | ('%1 is %2' % { k. v }) displayNl ].
    "=> fire is hot
     => water is wet"
    
    "删除 'water' -> 'wet'"
    hash removeKey: 'water'
    

    GNU Smalltalk在字符串类(String),和作为它的超类的数组式搜集类(ArrayedCollection)之间,又介入了特有的字符数组类(CharacterArray),它的%方法,将其接收者中具有的特殊转义序列,替换为由参数给出的搜集中的元素,其中%n被替代为这个搜集的第n个元素(1 <= n <= 9A <= n <= Z)。这里给它的搜集,是在{}之间包围的,当前Smalltalk变体一般都提供的动态数组,其中的元素可以在运行时间求值。

    块和迭代器[编辑]

    参数传递到是为闭包的一个块:

    "remember被绑定到一个块."
    remember := [ :name | ('Hello, %1!' % { name }) displayNl ].
    
    "时机成熟时 -- 调用这个闭包!"
    remember value: 'world'
    "=> Hello, world!"
    

    从一个方法返回由两个闭包构成的一个数组:

    Integer extend [
        asClosure [
            | value |
            value := self.
            ^{ [ :x | value := x ]. [ value ] }
        ]
    ].
     
    blocks := 10 asClosure.
    setter := blocks first.
    getter := blocks second.
    getter value.       "10"
    setter value: 21.   "21"
    getter value        "21"
    

    这里用extend为现存的类扩展新方法是GNU Smalltalk特有的语法。

    下面的考拉兹猜想例子,展示将两个块传递给接收者,并将结果信息发送回到调用者:

    Integer extend [
        ifEven: evenBlock ifOdd: oddBlock [
            ^self even
                ifTrue: [ evenBlock value: self ]
                ifFalse: [ oddBlock value: self ]
        ]
    ].
    
    10 ifEven: [ :n | n / 2 ] ifOdd: [ :n | n * 3 + 1 ]    "5"
    

    搜集类(Collection)的collect:方法,将接收者的每个元素传递给一个块来求值,返回这些结果的搜集。这类似于函数式编程语言中map函数。例如计算从110的平方:

    (1 to: 10) collect: [ :x | x squared ] "(1 4 9 16 25 36 49 64 81 100 )"
    

    可迭代类(Iterable)定义了由子类实现的do:方法,将一个块迭代于接收者的每个元素之上。这也被称为隐式迭代器。例如迭代于数组和区间之上:

    array := #(1 'hi' 3.14e0).
    array do: [ :item | item displayNl ].
    "=> 1"
    "=> hi"
    "=> 3.14"
    
    (3 to: 6) do: [ :item | item displayNl ]
    "=> 3"
    "=> 4"
    "=> 5"
    "=> 6"
    

    可迭代类的inject:into:方法,接受一个参数和一个块二者;它迭代于接收者的每个元素之上,在其上执行某个函数并保持结果为一个聚集。这类似于函数式编程语言中foldl函数。这个方法是凭借调用do:方法实现的。例如:

    #(1 3 5) inject: 10 into: [ :sum :element | sum + element ] "19"
    

    在第一个趟时,这个块接受10(要注入的实际参数)作为sum ,和1(这个数组的第一个元素)作为元素,结果为1111接着成为在下一趟时的sum,这时向它加上3得到1414接着加上5,最终返回19

    块可以和很多内建方法一起工作,下面例子向一个文件写一行文本,然后再读取它的每一行并显示:

    (File name: 'file.txt') withWriteStreamDo:
        [ :file | file nextPutAll: 'Wrote some text.'; nl ]
    
    (File name: 'file.txt') readStream linesDo:
        [ :each | each displayNl ] ; close
    "=> Wrote some text."
    

    文件类(File)的特有于GNU Smalltalk的name:方法,返回具有绝对路径的文件名。GNU Smalltalk有特有的文件路径类(FilePath),它的withWriteStreamDo:方法,对接收者调用writeStream方法打开一个只写的文件串流类(FileStream)实例,在其上调用一个块,并在这个块的动态范围结束处保证(ensure:)关闭这个串流;它的readStream方法,在接收者上打开一个只读的文件串流类(FileStream)实例。

    串流类(Stream)的特有于GNU Smalltalk的linesDo:方法,对它的接收者的每一行都求值它的参数块一次。 在GNU Smalltalk中,文件串流类(FileStream)的超类,不再是作为可定位串流类(PositionableStream)子类的读写串流类(ReadWriteStream),而是其特有的作为串流类(Stream)子类的文件描述符类(FileDescriptor),它的close方法关闭这个文件。

    [编辑]

    GNU Smalltalk建立新类采用特有的语法形式:

    超类名字 subclass: 新类名字 [
        | 诸实例变量 |
        pragmas
        消息模式1 [ 诸语句 ]
        消息模式2 [ 诸语句 ]
        ...
        类变量1 := 表达式.
        类变量2 := 表达式.
        ...
    ]
    

    类似的,为现存的类扩展新方法采用特有的语法形式:

    类表达式 extend [
        ...
    ]
    

    在Smalltalk有关书籍中有一个常见版式约定,将一个类中的方法引用为类名字 >> 方法名字,这不是Squeak/Pharo语法的一部分,GNU Smalltalk将类名字 class >> 方法名字作为定义类方法的语法形式。

    下面的代码定义叫做Person的一个类,这个类有两个实例变量nameage,它们有各自的变异子与访问子。定义了有两个关键字参数的类方法,用来创建新的类实例。定义了单独用age来进行比较的<方法,通过从Magnitude派生,这个类自动继承了所有的其他比较方法的定义。这个类还通过覆写printOn:的方式,定制了这个对象的打印(print)/显示(display)方式:

    Magnitude subclass: Person [
        | name age |
        Person class >> name: name age: age [
            ^self new name: name; age: age; yourself
        ]
    
        < aPerson [ ^self age < aPerson age ]
        name [ ^name ]
        name: value [ name := value ]
        age [ ^age ]
        age: value [ age := value ]
        printOn: aStream [ aStream nextPutAll: ('%1 (%2)' % { name. age }) ]
    ].
    
    group := {
        Person name: 'Dan' age: 23.
        Person name: 'Mark' age: 63.
        Person name: 'Cod' age: 16
    }.
    
    group asSortedCollection reverse
    

    这里用asSortedCollection方法对搜集进行排序,然后用reverse方法来做反转。最终结果是按age反序打印了三个人的信息:

    OrderedCollection (Mark (63) Dan (23) Cod (16) )
    

    异常[编辑]

    要发起能够捕获的异常,需要调用异常类(Exception)及其子类的signalsignal:方法。错误类(Error)表示不可恢复的致命错误,警告类(Warning)表示重要但可恢复的错误,停机类(Halt)表示通常是漏洞(bug)的可恢复错误。例如:

    Error signal.
    Error signal: 'Illegal arguments!'
    

    异常通过块闭包(BlockClosure)的on:do:方法来处理,还可以只捕获特定的异常(和它们的子类):

    [ 做些事情
    ] on: Exception do: [ :ex |
        处理ex中异常
    ]
    
    [ 做些事情
    ] on: Warning do: [ :ex |
        处理ex中异常
    ]
    

    处理器子句使用它能获得的异常对象,可以退出或恢复一个块;退出是缺省的,但也可以显式的指示:

    [ Error signal: 'foo' 
    ] on: Error do: [ :ex |
        ex return: 5
    ]
    
    (Warning signal: 'now what?') printNl     "=> nil"
    
    [ (Warning signal: 'now what?') printNl
    ] on: Warning do: [ :ex |
        ex resume: 5 
    ]                "=> 5"
    

    在因异常状况而要进入调试器,可以调用对象类(Object)的halt方法,或增加了一个消息参数的halt:方法;这二者实际上调用了对象类的error:方法,它通过原始操作停止执行及或英语And/or启动调试器,并展示这个错误消息:

    self halt. "halt encountered"
    self halt: 'This is a message'.
    self error: 'This is a message'
    

    参见[编辑]

    引用[编辑]

    1. ^ AUTHORS. [2022-02-10]. (原始内容存档于2022-03-18). 
    2. ^ index : smalltalk.git. [2022-02-10]. (原始内容存档于2022-03-07). 
    3. ^ Syntax of GNU Smalltalk. [2022-02-09]. (原始内容存档于2022-02-18). 
    4. ^ Computer Programming using GNU Smalltalk. [2022-02-09]. (原始内容存档于2022-04-06). 
    5. ^ Packages. [2022-02-09]. (原始内容存档于2022-02-18). 

    外部链接[编辑]