繁体   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