簡體   English   中英

如何在僅用於數字輸入的Smalltalk集合中編寫其他方法?

[英]How to write additional methods in Smalltalk Collections which work only for Numeric Inputs?

我想為數組類添加一個“average”方法。 但是,如果輸入數組包含字符/字符串/對象,則平均值沒有任何意義。 所以我需要檢查數組是否只包含整數/浮點數。

Smalltalk說數據類型檢查[檢查變量是否屬於特定數據類型,如int string array等...或者不是]是一種糟糕的編程方式。

那么實現這個的最佳方法是什么?

規范有點不完整。 您需要指定集合在使用非數字輸入時應顯示的行為。 存在大量可能期望的行為。 Smalltalk支持其中的大多數,除了靜態類型解決方案(在向數字集合添加非數字事物時拋出編譯時錯誤)。

  • 如果你想盡可能晚地捕獲非數字對象,你可能什么都不做 - 沒有算術方法的對象會在你對它們進行算術運算時發出自己的異常信號。
  • 如果要提前捕獲非數字元素,請實現一個集合類,以確保只能添加​​數字對象(可能通過添加非數字對象時發出異常信號)。
  • 您可能還希望為求和或平均值實現“寬容”方法,將非數字對象視為零值或不存在(對於#sum沒有區別,但對於#average,您只計算數字對象)。

至少在中有

Collection >> average
^ self sum / self size

Collections-arithmetic類別中。 使用靜態類型語言時,在向集合中添加非數字值時,您將被該語言命中。 在動態類型語言中,當您嘗試計算不合適元素的平均值時,您會嘗試發送+-/到不理解它的對象。

不要在想你放數據的地方,想一想你用它做什么。

如果你想做不同的事情,檢查類型是合理的,例如:

(obj isKindOf: Number) ifTrue: [:num| num doItForNum].
(obj isKindOf: Array ) ifTrue: [:arr| arr doItForArr].

但在這種情況下,您希望將類型檢查的邏輯移動到對象端。

所以最后它將是:

obj doIt.

然后你還會有類似的東西:

Number >> doIt
    "do something for number"

Array >> doIt
    "do something for array"

(brite的例子是printOn:方法)

我原以為Smalltalk的答案是為數字實現它,然后注意不要發送寵物的集合#sum或#average。 當然,如果后來成為寵物將其自身添加到另一只寵物或甚至是#average的答案的有用實現,那么這將取決於Pet或PetCollection的實現者。

當我將瑣碎的代數實現到我的圖像中時,我做了類似的事情。 它允許我在簡單的數學方程中混合數字,字符串和符號。 2 * #x導致2x。 x + y導致x + y。 通過想象錢包中發生的代數來試驗貨幣是一種有趣的方式。 進入我的圍牆我存款(5 x #USD)+(15 * #CAN)5USD + 15CAN。 給定一個在貨幣之間轉換的對象,然后我可以回答CAN或USD中的總數。

我們實際上將它用於供應鏈軟件以解決簡單的權重和度量。 如果采購訂單說它將向XUSD / 1TON支付某些東西,但供應商發送同樣數量的英尺,那么為了驗證貨運價值,我們需要在噸和英尺 - 磅之間進行轉換。 讓庫減少等式,我們能夠產生結果而不會騷擾輸入數據,或者不必提出代表噸和英尺磅或其他任何東西的新對象。

我對圖書館抱有很大的抱負(這很簡單)但是,唉,2008年整個事情已經消失了......

“我想在數組類中添加一個”average“方法。但是如果輸入數組包含字符/字符串/對象,則平均值沒有任何意義。所以我需要檢查數組是否只包含整數/浮點數。”

在過濾掉非數字對象時,有許多方法可以實現數組總和的平均值。

首先,我將它提升到Collection類,使其成為一種更通用的方法,以便它可以找到更多的重用案例。 其次我認為它是數字的通用而不僅僅是浮點數和整數,哦它對那些人也適用於分數。 如果集合數組列表中有數字,則結果將是浮點平均值。

(1)當向對象添加對象時,測試它們以確保它們是數字,並且只有在它們是數字時才添加它們。 這是我的首選解決方案。

(2)使用Collection #select:instance方法過濾掉非數字,只留下單獨集合中的數字。 這樣可以以新集合為代價輕松生活(除非您關注大型列表和內存問題,否則這樣很好)。 這是非常有效,易於操作的,也是在對集合執行某些操作之前過濾集合的常用解決方案。 打開一個Smalltalk並查找#select的所有發件人:查看其他示例。

| list numberList sum average |
list := { 100. 50. 'string'. Object new. 1. 90. 2/3. 88. -74. 'yup' }.
numberList := list select: [ :each | each isNumber ].
sum := numberList sum.
average := sum / (numberList size) asFloat.

使用“print it”執行上面的代碼將為示例數組列表生成以下代碼:

 36.523809523809526

但是,如果數字列表的大小為零,則換句話說為空,那么您將使用上述代碼獲得除以零的異常。 此版本也不作為實例方法在Collection類上。

(3)為Collection類編寫一個實例方法,為你做平均工作。 此解決方案不使用select,因為它創建了中間集合,如果列表非常大,則需要收集大量額外垃圾。 此版本僅循環覆蓋結果的現有集合。 簡單,有效。 它還解決了沒有數字來計算的情況,在這種情況下它返回nil對象而不是數字平均值。

收集方法:#computeAverage

"Compute the average of all the numbers in the collection. If no numbers are present return the nil object to indicate so, otherwise return the average as a floating point number."

| sum count average |
sum := 0.
count := 0.
self do: [ :each |
     each isNumber ifTrue: [
        count := count +1.
        sum := sum + each.  
    ]
].
count > 0 ifTrue: [ 
    ^average := sum / count asFloat
] ifFalse: [ 
    ^nil
]

請注意,變量“average”僅用於顯示數學,實際上並不需要。

然后使用上面的方法如下:

| list averageOrNil |
list := { 100. 50. 'string'. Object new. 1. 90. 2/3. 88. -74. 'yup' }.
averageOrNil := list computeAverage.
averageOrNil ifNotNil: [ "got the average" ] ifNil: [ "there were no numbers in the list"

或者您可以像這樣使用它:

{ 
    100. 50. 'string'. Object new. 1. 90. 2/3. 88. -74. 'yup' 
} computeAverage 
    ifNotNil: [:average | 
        Transcript show: 'Average of list is: ', average printString 
    ] 
    ifNil: [Transcript show: 'No numbers to average' ].

當然,如果您確定列表中有數字,那么您將無法獲得nil對象的特殊情況,並且您不需要使用if消息進行相應的分支。


運行時數據類型/類檢查

至於你提出的問題,“Smalltalk說數據類型檢查[檢查變量是否屬於特定的數據類型,如int string array等...或者不是]是一種糟糕的編程方式”,有辦法做更好的事情。其他。

例如,雖然可以使用#isKindOf:Number來詢問每個元素是否不是在運行時確定“類型”或“類”的最佳方法,因為它通過預定類型或類將其作為參數鎖定到#isKindOf : 信息。

使用諸如#isNumber之類的“is”“class”方法會更好,這樣任何一個數字的類都會返回true,而所有其他非數字的對象都會返回false。

Smalltalk在確定事物的類型或類別時的一個主要觀點是,最好使用消息發送,消息是各種類型/類理解但行為不同而不是使用顯式類型/類檢查(如果有的話)可能。

#isNumber方法是Pharo Smalltalk中Number類的實例方法,它在Object實例版本上返回true,返回false。

使用多態消息發送可以實現更大的靈活性,並消除通常過於程序化或過於具體的代碼。 當然最好避免這樣做,但現實在各種應用程序中都會出現,你必須盡力做到最好。

這不是你在Smalltalk中所做的事情。 您可以從上述評論中獲取建議並“使其工作”,但這個想法是錯誤的(從Smalltalk的角度來看)。

“Smalltalk”要做的就是創建一個可以為你執行所有這些操作的類 - 計算平均值,平均值,模式等。然后類可以對數字輸入進行正確的檢查,你可以編寫如何它會回應糟糕的輸入。 該類將使用普通的舊數組,或列表或其他東西。 該類的名稱將清楚它的用途是什么。 然后,該類可以成為部署的一部分,並可根據需要導出/導入到不同的映像。

創建一個新的集合類; 可能是Array的子類,也可能是OrderedCollection的子類,具體取決於您想要的集合相關行為。

在新類' at:put:和/或add:方法中測試#isNumber的新項,如果失敗則返回錯誤。

現在你有一個集合,你可以保證只有數字對象和nils。 在您不需要處理嘗試將Sealion添加到金橘的知識中實現您所需的功能。 但要注意細節; 例如,如果您創建一個大小為10的WonderNumericArray並在其中插入兩個值,那么當您對數組求平均值時,您想要WonderNumericArray兩個項相加並除以2或10嗎?

暫無
暫無

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

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