簡體   English   中英

如何在Pharo Smalltalk中實現切換

[英]How to implement a switch in Pharo Smalltalk

我試圖解析一個命令和一個int,以使“烏龜”動作在板上。 我有點不知所措,因為它沒有引發異常,而且我什至不知道如何在沒有調試器的情況下打開調試器。

我的代碼:

"only should begin parsing on new line"
endsWithNewLine:= aTurtleProgram endsWith: String cr.
endsWithNewLine ifTrue:[
    "splits commands based on new lines"
    commands := aTurtleProgram splitOn: String cr.
    commands do:[:com |
        "should split into command and value for command"
        i := com splitOn: ' '.
        "command"
        bo := ci at: 1.
        val := ci at: 2.
        "value for command"
        valInt := val asInteger.
        ^ bo = 'down' "attempted switch"
            ifTrue: [ turtle down: valInt ]
            ifFalse: [
              bo = 'left'
                  ifTrue: [ turtle left: valInt ]
                  ifFalse: [
                    bo = 'right'
                        ifTrue: [ turtle right: valInt ]
                        ifFalse: [
                          bo = 'up'
                              ifTrue: [ turtle up: valInt ]
                              ifFalse: [ self assert: false ] ] ] ] ].

您可以按照自己的方式進行操作,也可以通過在代碼中插入self halt語句來打開調試器。

通常情況下, if S,而且案例sytle if S,是一個不好的跡象。 所以你可以做的是打破了功能集成到諸如類DownMoveLeftMove等,然后當你打電話時,例如,每個類將實現其自身的功能execute:方法將做什么是由命令需要的。 但是,對於您而言,這種方法將很麻煩。 此外,您的動作非常簡單。

您可以使用帶有定義的字典。 因此,假設您定義了一個實例變量或類變量:

moveActions := {
  'down' -> [ :dist | turtle down: dist ] .
  'left' -> [ :dist | turtle left: dist ] .
  ... } asDictionary

然后在您的代碼中執行: (moveActions at: bo) value: valInt 此代碼段將為您提供字符串(鍵)的塊(值),然后使用整數對塊進行評估。

另一方面,由於操作模式相同且僅消息有所更改,因此您只能在字典中映射消息名稱:

moveActions := {
  'down' -> #down: .
  'left' -> #left: .
  ... } asDictionary

然后,您可以要求烏龜執行由字符串動態給出的消息:

`turtle perform: (moveActions at: bo) with: valInt`

另外,如果您想依靠閱讀的命令和發送給烏龜的消息之間的相似性,則可以動態地編寫消息字符串:

`turtle perform: (bo, ':') asSymbol with: valInt`

請注意,在這種情況下不建議這樣做,因為首先,您正在將用戶輸入和代碼耦合在一起,即,如果您決定將用戶命令從down更改為moveDown ,則必須從down:更改方法名稱。 down:moveDown: 另外,這種方法還允許用戶將錯誤代碼“注入”到您的系統中,因為他可以編寫諸如become 42這樣的命令,這將導致代碼:

`turtle perform: #become: with: 42`

這將在烏龜對象和42之間交換指針。或者您可以考慮更糟的情況。 但我希望這種元游覽對您有好處。 :)

在Smalltalk中,您不使用switch語句。 相反,您使用“案例方法”(我認為術語是Ken Beck引入的,但我不確定)。

在您的情況下,將是這樣的:

method1
    "only should begin parsing on new line"
    endsWithNewLine:= aTurtleProgram endsWith: String cr.
    endsWithNewLine ifTrue:[
    "splits commands based on new lines"
    commands := aTurtleProgram splitOn: String cr.
    commands do:[ :eachCommand | 
        | tuple direction value |

        tuple := eachCommand splitOn: ' '.
        direction := tuple first.
        value := tuple second asInteger.
        self moveTurtleDirection: direction value: value ].

moveTurtleDirection: direction value: value
    direction = 'down'  ifTrue: [ ^turtle down: value ].
    direction = 'left'  ifTrue: [ ^turtle left: value ].
    direction = 'right' ifTrue: [ ^turtle right: value ].
    direction = 'up'    ifTrue: [ ^turtle up: value ].

    self error: 'Invalid direction'.

如您所見,這更加清楚了,您無需應用“小講堂魔術”即可獲得高效的設計。 這還有一個優點,就是清晰,執行速度快,並且易於被編譯器 JIT優化:)

只是列舉了另一種可能性:與其自己編寫解析器,不如使用Smalltalk中可用的一種ParserGenerator(PetitParser,OMeta,Smacc,Xtreams等)。

這是Xtreams https://code.google.com/p/xtreams/wiki/Parsing的示例(鏈接可能很快就會消失,但是我沒有更新的...),可以解析PEG格式( http://en.wikipedia.org/wiki/Parsing_expression_grammar )。

首先,您在字符串中定義語法:

grammar := '
    Number   <- [0-9]+
    Up  <- "up" Number
    Down    <- "down" Number
    Left    <- "left" Number
    Right   <- "right" Number
    Command <- Up / Down / Left / Right
'.

然后定義一個解釋器來移動烏龜:

PEGActor subclass: #TurtleInterpreter
    instanceVariableNames: ''
    classVariableNames: ''
    poolDictionaries: ''
    category: 'Test-Parser'.

通過幾種方法將解釋器連接到aTurtle,並通過所謂的編譯指示(注解)將動作連接到上述語法規則:

turtle: aTurtle
    turtle := aTurtle

Number: digits
    <action: 'Number'>
    ^digits inject: 0 into: [ :total :digit | total * 10 + ('0123456789' indexOf: digit) - 1 ]

Up: aNumber
    <action: 'Up'>
    turtle moveUp: aNumber

Down: aNumber
    <action: 'Down'>
    turtle moveDown: aNumber

Left: aNumber
    <action: 'Left'>
    turtle moveLeft: aNumber

Right: aNumber
    <action: 'Right'>
    turtle moveRight: aNumber

然后,您只需創建一個解析器並將其連接到此解釋器:

parser := PEGParser parserPEG
    parse: 'Grammar'
    stream: grammar
    actor: PEGParserParser new.
interpreter := TurtleInterpreter new turtle: Turtle new.    
parser parse: 'Command' stream: 'left24' actor: interpreter.

我讓您發現如何指定空格,換行符或命令序列,但是您會發現使用這種框架可以使代碼脫鈎並易於擴展:一條新命令=語法描述中的一行+解釋器中用於連接至的一種方法龜行動...

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM