[英]How to write additional methods in Smalltalk Collections which work only for Numeric Inputs?
我想为数组类添加一个“average”方法。 但是,如果输入数组包含字符/字符串/对象,则平均值没有任何意义。 所以我需要检查数组是否只包含整数/浮点数。
Smalltalk说数据类型检查[检查变量是否属于特定数据类型,如int string array等...或者不是]是一种糟糕的编程方式。
那么实现这个的最佳方法是什么?
规范有点不完整。 您需要指定集合在使用非数字输入时应显示的行为。 存在大量可能期望的行为。 Smalltalk支持其中的大多数,除了静态类型解决方案(在向数字集合添加非数字事物时抛出编译时错误)。
至少在pharo中有
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.