解释一下Smalltalk的一段代码?
我无法理解这段 Smalltalk 代码:
[(line := self upTo: Character cr) size = 0] whileTrue.
有人能帮忙解释一下吗?
I cannot understand this piece of Smalltalk code:
[(line := self upTo: Character cr) size = 0] whileTrue.
Can anybody help explain it?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
如果您有代码来源的映像,那么一件简单的事情就是在其上运行调试器并单步执行。
如果您在上下文之外遇到了代码,例如邮件列表帖子,那么您可以浏览其中一条消息的实现者并查看它的作用。例如,#size 和 #whileTrue 是非常标准的,所以我们现在将跳过它们,但 #upTo: 听起来很有趣。它让我想起了流方法,并在其上调用实现者确认(在 Pharo 1.1.1 中),ReadStream 定义了它。没有方法注释,但 OmniBrowser 在方法名称旁边显示一个小箭头,表明它是在超类中定义的。如果我们检查直接超类 PositionableStream,有一个很好的方法注释解释了该方法的作用,该方法从流中绘制,直到到达参数指定的对象。
现在,如果我们逻辑地解析代码,似乎是:
因此,代码会跳过所有空行并返回第一个非空行。为了确认这一点,我们可以向它传递一个多行字符串流并像这样运行它:
One easy thing to do, if you have the image where the code came from, is run a debugger on it and step through.
If you came across the code out of context, like a mailing list post, then you could browse implementers of one of the messages and see what it does. For example, #size and #whileTrue are pretty standard, so we'll skip those for now, but #upTo: sounds interesting. It reminds me of the stream methods, and bringing up implementors on it confirms that (in Pharo 1.1.1), ReadStream defines it. There is no method comment, but OmniBrowser shows a little arrow next to the method name indicating that it is defined in a superclass. If we check the immediate superclass, PositionableStream, there is a good method comment explaining what the method does, which is draw from the stream until reaching the object specified by the argument.
Now, if we parse the code logically, it seems that it:
So, the code skips all empty lines and returns the first non-empty one. To confirm, we could pass it a stream on a multi-line string and run it like so:
这是更具可读性:
如果 feof 或任何其他错误,上面的表达式有缺陷, line==NULL
Smalltalk 表达式也是如此,如果遇到流结束, upTo: 将回答一个空集合,并且您将遇到无限循环,除非您有一个特殊的流在流结束时引发错误...尝试
Is this more readable:
Above expression has a flaw if feof or any other error, line==NULL
So has the Smalltalk expression, if end of stream is encountered, upTo: will answer an empty collection, and you'll have an infinite loop, unless you have a special stream that raises an Error on end of stream... Try
Smalltalk的优先规则是
第一:一元消息
第二:二进制消息
第三:关键词信息
最后:从左到右
从左到右的顺序可以通过使用括号(即 ( ) 括号)来更改。首先计算括号对内的表达式。
在括号嵌套的情况下,首先计算最里面的括号,然后向外计算外括号,最后计算括号外表达式的剩余部分。
由于从左到右的强烈倾向,我经常发现从右到左阅读表达式很有用。
因此,对于
[(line := self upTo: Character cr) size = 0] whileTrue.
从结尾回到开头,我们可以得到以下解释。
.
结束表达式。相当于 C 或 Java 中的;
whileTrue
它左边紧邻的是什么?]
块对象的闭包。因此
whileTrue
是发送到块[
...]
的一元消息即继续执行此块,而块的计算结果为 true
块返回块中最后一个表达式计算的结果。
块中的最后一个表达式是
size = 0
比较。和一个二进制消息。size
通常是发送给接收者的一元消息。因此,我们要检查某物的大小
,看看它是否为0
。如果某物的大小
为0
,则继续。我们要检查的
大小
是什么?紧邻消息名称左侧的表达式。size
左侧是(line := self upTo: Character cr)
这就是我们想要知道的大小。
所以,是时候把这个表达放在刀下了。
(line := self upTo: Character cr)
是一个赋值。line
将得到以下结果self upTo:分配给它的字符cr
。该表达式的右端是什么?
cr
这是一元消息,因此具有最高优先级。它被发送到什么地方。即cr
消息的接收者是什么?紧邻其左侧的是
Character
。因此,向Character 类发送消息cr
其计算结果为Character 类的一个实例,其值为13——即回车符。所以现在我们要解决的是
self upTo: aCarriageReturn
如果
self
- 接收self upTo: aCarriageReturn
消息的对象 - 不理解该消息namesizeUpto:
它将引发异常。因此,如果这是来自工作系统的代码,我们可以推断
self
必须是一个能够理解sizeUpTo:
的对象。 此时,我常常想搜索按摩名称以查看哪些类在其知道和理解的消息名称列表中具有名为sizeUpto:
的消息(即它们的消息 protocol )。(在这种情况下,它对我没有任何好处 - 它不是我的 Smalltalk 系统中任何类中的方法)。
但是 self 似乎被要求处理包含(可能)许多回车符的字符串。
因此,返回 aCharacterString 的第一部分,直到第一个回车符。
如果 aCharacterString 从开始到第一个回车符的长度为零,则继续执行所有操作。
因此,我们似乎正在处理多个以 cr 结尾的字符串的串联,并依次处理每个字符串,直到找到一个不为空的字符串(除了回车符之外),并将其分配给
line< /代码>
The precedence rules of Smalltalk are
first: unary messages
second: binary messages
third: keyword messages
last: left to right
This order of left to right, can be changed by using parenthesis i.e. ( ) brackets. The expression within the pair of brackets is evaluated first.
Where brackets are nested, the inner-most bracket is is evaluated first, then work outwards in towards the outer bracket, and finally the remains of the expression outside the brackets.
Because of the strong left-to-right tendency, I often find it useful to read the expression from right to left.
So for
[(line := self upTo: Character cr) size = 0] whileTrue.
Approaching it from the end back to beginning gives us the following interpretation.
.
End the expression. Equivalent to;
in C or JavawhileTrue
What's immediately to the left of it?]
the closure of a block object.So
whileTrue
is a unary message being sent to the block[
...]
i.e. keep doing this block, while the block evaluates to true
A block returns the result of the last expression evaluated in the block.
The last expression in the block is
size = 0
a comparison. And a binary message.size
is generally a unary message sent to a receiver. So we're checking thesize
of something, to see if it is0
. If the something has asize
of0
, keep going.What is it we are checking the
size
of? The expression immediately to the left of the message name. To the left ofsize
is(line := self upTo: Character cr)
That's what we want to know the size of.
So, time to put this expression under the knife.
(line := self upTo: Character cr)
is an assignment.line
is going have the result ofself upTo: Character cr
assigned to it.What's at the right-hand end of that expression?
cr
It's a unary message, so has highest precedence. What does it get sent to. i.e. what is the receiver for thecr
message?Immediately to its left is
Character
. So send the Character class the messagecr
This evaluates to an instance of class Character with the value 13 - i.e. a carriage return character.So now we're down to
self upTo: aCarriageReturn
If
self
- the object receiving theself upTo: aCarriageReturn
message - does not understand the message namesizeUpto:
it will raise an exception.So if this is code from a working system, we can infer that
self
has to be an object that understandssizeUpTo:
At this point, I am often tempted to search for the massage name to see which Classes have the message namedsizeUpto:
in their list of message names they know and understand (i.e. their message protocol ).(In this case, it did me no good - it's not a method in any of the classes in my Smalltalk system).
But
self
appears to be being asked to deal with a character string that contains (potentially) many many carriage returns.So, return the first part of
aCharacterString
, as far as the first carriage-return.If the length of aCharacterString from the start to the first carriage return is zero, keep going and do it all again.
So it seems to be we're dealing with a concatenation of multiple cr-terminated strings, and processing each one in turn until we find one that's not empty (apart from its carriage- return), and assigning it to
line
我发现不同风格的格式有帮助。所以
我不会将其重新格式化为
I find that a different style of formatting helps. So instead of
I would reformat this to
我个人不太喜欢 Smalltalk 的一件事是,虽然消息传递始终用于执行几乎所有操作,但有时很难确定哪些消息正在发送给哪个接收者。这是因为 Smalltalk 在消息发送周围没有任何分隔符(例如 Objective-C),而是允许您在遵循一组优先级规则的同时链接消息发送,这些规则类似于“消息发送从左到右解释” ,除非用括号分隔,否则首先评估具有多个关键字的消息,然后评估二进制关键字消息,然后评估一元关键字消息,然后评估没有关键字的消息。”当然,使用临时变量甚至只是括号来明确消息的顺序可以减少您必须考虑这种操作顺序的情况。下面是上述代码的示例,分为多行,使用临时变量和括号进行显式消息排序以提高可读性。我认为这对于代码的意图更加清晰:
所以基本上,line 是当您连接字符串 self 中的字符直到回车符(字符 cr)时创建的字符串。
然后,我们检查行的字符大小,并检查它是否等于 0,因为我们将其放入块(括号)中,所以我们可以向它发送 whileTrue,它会重新评估块中的条件,直到返回 true。所以,是的, whileTrue 如果被称为 doWhileTrue 或类似的东西,确实会更清楚。
希望有帮助。
One thing about Smalltalk that I'm personally not a huge fan of is that, while message passing is used consistently to do nearly everything, it can sometimes be difficult to determine what message is being sent to what receiver. This is because Smalltalk doesn't have any delimiters around message sends (such as Objective-C for example) and instead allows you to chain message sends while following a set of precedence rules which go something like "message sends are interpreted from left to right, and unless delimited by parentheses, messages with many keywords are evaluated first, then binary keyword messages, then unary, and then no keyword ones." Of course using temporary variables or even just parentheses to make the order of the messages explicit can reduce the number of situations where you have to think about this order of operations. Here is an example of the above code, split up into multiple lines, using temp variables and parenthesis for explicit message ordering for readability. I think this is a bit clearer about the intent of the code:
So basically, line is the string created when you concatenate the characters in string self up until the carriage return character (Character cr).
Then, we check line's size in characters, and check if that's equal to 0, and because we put this in a block (brackets), we can send it a whileTrue, which re-evaluates the condition in the block until it returns true. So, yeah whileTrue really would be clearer if it was called doWhileTrue or something like that.
Hope that helps.