1

我认为我的问题主要是语法,但可能是我对类层次结构的整体理解。基本上它是一个 Deck 类,其中包含一个填充了 Card 对象的数组,Card 是 Deck 的子类,所以 Deck 应该能够使用 Card 的块和方法,对吧?如果是这样,我会在试图调用它时把语法弄得一团糟。我正在使用嵌套的 while 循环来填充数组,但我希望 Card 对象的每个实例都具有该卡的花色和等级,而不是仅仅打印“a Card”。我离开了我试图让 Card 对象成为另一个大小为 2 的数组来保存 Suit 和 Rank 的地方,但是我的 gst 编译器说它需要一个“对象”,所以很明显我做错了什么。我粘贴了我的代码,这样你就可以看到我在说什么。

"The Deck object class is a deck of 52 Card objects. "
Object subclass: Deck [
    | Content |
        <comment: 'I represent of Deck of Cards'>
    Deck class >> new [
        <category: 'instance creation'>
        | r |
        r := super new . 
        Transcript show: 'start ' .
        r init .
        ^r
    ] 

    init [
        <category: 'initialization'>

        |a b c|
        Content := Array new: 52 .
        a := 1 .
        c := 1 . 



        [a <= 4] whileTrue:[
            b := 1 . 
            [b <= 13] whileTrue:[
                |card|
                card := Card new .
                Card := Array new: 2 .            "This is where I'm trying to use the Card class blocks to make the Card objects have Rank and Suit" 
                Card at: 1 put: Card setRank: b| . "and here the rank"
                Card at: 2 put: Card getSuit: a| . "and the suit"
                Content at: c put: card .

                b := b + 1 .
                c := c + 1 . 

            ].

            a := a + 1 . 

        ].
     Content printNl . 
    ]

] .

"The Card object has subclass Suit and a FaceValue array of Suit and Rank. "
Object subclass: Card [
    | Suit Rank |
        <comment: 'I represent a playing Card' >
    init [
        <category: 'initialization'>
        Suit := Club .
        Rank := 1 .
        Transcript show: 'nCard ' .
        ^super init
    ]
    getSuit: suitVal [
        suitVal = 1 ifTrue: [Suit := Club] . 
        suitVal = 2 ifTrue: [Suit := Diamond] . 
        suitVal = 3 ifTrue: [Suit := Heart] . 
        suitVal = 4 ifTrue: [Suit := Spade] . 
        ^Suit 
    ] "getSuit"

    setRank: rankVal [
    Rank := rankVal . 
    ^Rank
    ]
] 

z := Deck new .
4

2 回答 2

3

编辑过一次- 由于 fede s. 的评论

欢迎来到 SO。

免责声明:我使用的是 Smalltalk/X-jv 分支,它与 smalltalk 与 gnu-smalltalk 不同,所以我不是 gnu-smalltalk 专家。

我会指出我发现的一些缺陷。有太多要指出的。我会给你一些一般的想法。

  1. 我一般不建议使用a, b, c...z作为变量。如果您稍后再返回代码,您将无法理解它。

  2. 对于变量,请使用小写,content而不是Content. 第一个大写字母是为全局变量保留的。在您的用例中,这将是一个类名(不要混用)。

  3. 如果要创建一个新实例,请像这样使用它:aCard := Card new.

  4. 不要在您的#init(初始化)方法中创建整个应用程序逻辑!你应该用小而易读的方法来破坏你的代码。

您的 init 应遵循以下原则:

  Deck extend [
      init [
          <category: 'initialization'>
          content := Array new: 52.
          Transcript show: 'Initializing Deck...'.
      ]
  ]

这将创建一个#init带有实例变量的方法content。不要忘记为变量创建访问器。阅读 gnu-smalltalk 和创建实例方法的指南

  1. 您应该对whileTrue:循环发表评论。为什么有人要猜测限制的原因?

     [a <= 4] whileTrue:[
           b := 1 . 
           [b <= 13] whileTrue:[
     ...
    
  2. 有充分的理由重新定义new消息。消息可以Transcript是 ini init

    Deck class >> new [
         <category: 'instance creation'>
         | r |
         r := self new . 
         Transcript show: 'start  '.
         r init .
         ^r
    ] 
    

为什么要重新定义新的?如果你有一个对象,Object subclass:那么它已经理解了new消息。

在您的代码底部z := Deck new.(我建议使用 eg myDeck := Deck new.)。如果你想运行初始化,你会很简单myDeck init

  1. 你为什么要Card >> init退货^super init?也许你想做第一行super init(读取\加载超类初始化)然后返回^ self?很难说。

  2. Suit := Club . 这意味着什么?您是否以某种方式创建了一个新对象?(缺少#new消息)。您是否要尝试分配字符串?那时应该是suit := 'Club'.。(所有Sunit变量分配也是如此)。

更多的只是从水晶球中阅读。尝试阅读有关 Smalltalk 的更多信息,我希望我的提示对您有所帮助。

于 2019-03-18T08:13:29.737 回答
2

一点更正:你不调用一个块,你调用一个方法。

您提交的代码中唯一的块是 whileTrue: 的参数,并且是该方法私有的(不能从外部访问)。

这种混乱不是你的错。这是由于此文件格式用于描述方法的块表示法。就个人而言,我不喜欢它,我觉得它比有用更令人困惑(除了看起来像更主流的语言,向基于文件的语言致敬)。

那么如何调用方法呢?您通过发送消息来做到这一点,没有其他方法。您所能做的就是发送消息。Smalltalk 是面向消息的。一直是消息。

当接收到消息时,对象会在其类方法字典中查找消息选择器。如果没有,它将在超类中查找,等等……但这是对象自己的事情。

所以最后,你并没有真正考虑调用方法,因为不同的对象可以用不同的方法响应相同的消息。您必须考虑将任务委托给专门的对象,即消息和相关合同。

那么合约是什么?你想要一张有花色(梅花、黑桃……)和等级(1 到 13)的牌。所以你用这两个实例变量创建了一个 Card 类。到目前为止,一切都很好。

然后你想创建你的 52 张牌来填充牌组。那就是实例化 Card 类。你是怎样做的?您已将消息发送new到 Card 类。您可以创建一个更合适的类消息,作为 Card 构造函数,给定一套西装和一个等级。

那会是这样的

Card class >> newWithSuit: aSuit rank: anInteger
    [<category: 'instance creation'>
    ^self basicNew setSuit: aSuit rank: anInteger ]

setSuit: aSuit rank: anInteger
    [<category: 'private'>
    suit := aSuit.
    rank := anInteger ]

然后您不需要定义单独的设置器(getSuit:以及setRank:在您的代码中),因为您可能不想在之后更改这些属性,除非在构建时。这就是为什么我通常更喜欢我归类为“私人”的单个二传手。这只是一个约定,但这就是我们在 Smalltalk 中定义契约的方式,通过一组非正式约定,以及通过方法和类注释(以及 SUnit TestCase,特别是如果您采用测试驱动设计)。

您已决定将等级编码为从 1 到 13 的整数。没关系。但是您还必须决定如何代表诉讼。您的代码不清楚,因为混合了整数(1 到 4)和未声明的变量(俱乐部、黑桃,...)。这些是全局变量吗?

然后,您不必在大小为 2 的数组中对这些花色和排名进行编码,这绝对没有必要,因为所有这些数据都已包含在 Card 对象中。所以你会在甲板上填满:

content at: deckRank put: (Card newWithSuit: suitNumber rank: rankNumber).

注意括号:您使用两个参数发送消息at:put:到,另一个消息发送返回的值,我们希望它是正确初始化的 Card 实例。contentdeckRank(Card newWithSuit: suitNumber rank: rankNumber)

在您提供的代码中,您 使用 3 个参数向类Card at: 1 put: Card setRank: b发送消息,整数文字 1、类和变量指向的对象。这可能不是你的意图。后面还有一个栏,对我来说看起来不太像正确的语法。条用于描绘临时变量,或将块参数与块内的块指令分开,或者也可以是二进制消息,但在这种情况下需要一个参数(每个二进制消息,如 + - * / 都有一个接收器和一个参数)。at:put:setRank:CardCardb|b

我希望我给了您一些有用的提示,但也许您必须阅读并应用一些分步 Smalltalk 教程才能更好地熟悉这些基本概念。

于 2019-03-20T09:59:31.757 回答