是否存在我们无法迭代“反向范围”的原因?在红宝石中?
我尝试使用 Range 和 each
向后迭代:
(4..0).each do |i|
puts i
end
==> 4..0
通过 0..4
迭代写入数字。在另一个范围r = 4..0
似乎没问题,r.first == 4
,r.last == 0
。
我觉得奇怪的是上面的构造没有产生预期的结果。其原因何在?在什么情况下这种行为是合理的?
I tried to iterate backwards with using a Range and each
:
(4..0).each do |i|
puts i
end
==> 4..0
Iteration through 0..4
writes the numbers. On the other Range r = 4..0
seems to be ok, r.first == 4
, r.last == 0
.
It seems to be strange to me that the construct above does not produce the expected result. What is the a reason for that? What are the situations when this behaviour is reasonable?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(13)
范围就是这样:由其开始和结束而不是由其内容定义的东西。在一般情况下,在一定范围内“迭代”并没有真正意义。例如,考虑如何“迭代”两个日期产生的范围。你会按天迭代吗?按月?按年?按周?它没有明确定义。 IMO,它允许向前范围的事实应该仅被视为一种方便的方法。
如果你想在这样的范围内向后迭代,你总是可以使用
downto
:这里有 其他人的更多想法,解释为什么既允许迭代又一致处理反向范围很难。
A range is just that: something defined by its start and end, not by its contents. "Iterating" over a range doesn't really make sense in a general case. Consider, for example, how you would "iterate" over the range produced by two dates. Would you iterate by day? by month? by year? by week? It's not well-defined. IMO, the fact that it's allowed for forward ranges should be viewed as a convenience method only.
If you want to iterate backwards over a range like that, you can always use
downto
:Here are some more thoughts from others on why it's tough to both allow iteration and consistently deal with reverse-ranges.
向后迭代范围的
(0..1).reverse_each
怎么样?How about
(0..1).reverse_each
which iterates the range backwards?在 Ruby 中使用
each
迭代范围会调用该范围中第一个对象的succ
方法。5 超出了范围。
你可以用这个 hack 来模拟反向迭代:
John 指出,如果它跨越 0,这将不起作用。这将:
不能说我真的喜欢它们中的任何一个,因为它们有点模糊了意图。
Iterating over a range in Ruby with
each
calls thesucc
method on the first object in the range.And 5 is outside the range.
You can simulate reverse iteration with this hack:
John pointed out that this will not work if it spans 0. This would:
Can't say I really like any of them because they kind of obscure the intent.
另一种方法是
(1..10).to_a.reverse
Another way is
(1..10).to_a.reverse
根据《Ruby编程》一书,Range对象存储范围的两个端点,并使用
.succ
成员生成中间值。根据您在范围内使用的数据类型类型,您始终可以创建Integer
的子类并重新定义.succ
成员,使其充当反向迭代器(您可能还想重新定义.next
)。您也可以在不使用范围的情况下获得您正在寻找的结果。试试这个:
这将从 4 步进到 0,步长为 -1。但是,我不知道这是否适用于除整数参数之外的任何内容。
According to the book "Programming Ruby", the Range object stores the two endpoints of the range and uses the
.succ
member to generate the intermediate values. Depending on what kind of data type you are using in your range, you can always create a subclass ofInteger
and re-define the.succ
member so that it acts like a reverse iterator (you would probably also want to re-define.next
as well).You can also achieve the results you are looking for without using a Range. Try this:
This will step from 4 to 0 in steps of -1. However, I don't know if this will work for anything except Integer arguments.
您甚至可以使用
for
循环:打印:
You can even use a
for
loop:which prints:
如果列表不是那么大。
我认为
[*0..4].reverse.each { |i|放我 }
是最简单的方法。
if list is not that big.
i think
[*0..4].reverse.each { |i| puts i }
is simplest way.
正如 bta 所说,原因是
Range#each
将succ
发送到其开头,然后发送到succ
调用的结果,依此类推直到结果大于最终值。您无法通过调用succ
从 4 到 0,事实上您的起点已经大于终点。As bta said, the reason is that
Range#each
sendssucc
to its beginning, then to the result of thatsucc
call, and so on until the result is greater than the end value. You can't get from 4 to 0 by callingsucc
, and in fact you already start out greater than the end.我添加了另一种可能性如何实现反向范围迭代。我不使用它,但它是一种可能性。猴子修补 ruby 核心对象有点冒险。
I add one another possibility how to realise iteration over reverse Range. I do not use it, but it is a possibility. It is a bit risky to monkey patch ruby core objects.
OP 写道
不是“能做到吗?”但要回答在实际提出的问题之前没有提出的问题:
由于reverse_each声称可以构建整个数组,因此downto显然会更有效。事实上,语言设计者甚至可以考虑实现类似的东西,这与所提出的实际问题的答案有关。
回答实际提出的问题...
原因是因为 Ruby 是一种总是令人惊奇的语言。有些惊喜是令人愉快的,但也有很多行为是彻底被破坏的。即使以下示例中的一些示例已被新版本纠正,但还有很多其他示例,它们仍然是对原始设计思维方式的控诉:
结果为“”,但
结果为
您可能会期望 <<和 push 与追加到数组时相同,但
您可能会期望 'grep' 的行为类似于其 Unix 命令行等效项,但它确实 === 匹配而不是 =~,尽管它的名字如此。
各种方法意外地互为别名,因此您必须学习同一事物的多个名称 - 例如
find
和detect
- 即使您确实像大多数开发人员一样并且只使用其中之一。size
、count
和length
的情况大致相同,除了定义各自不同的类,或者没有定义一两个的类之外。全部。除非有人实现了其他东西 - 比如核心方法
tap
已在各种自动化库中重新定义以在屏幕上按下某些内容。祝你好运,找出发生了什么,特别是如果其他模块所需的某个模块已经欺骗了另一个模块来执行未记录的操作。环境变量对象 ENV 不支持“合并”,因此您必须编写
作为奖励,如果您改变主意,您甚至可以重新定义您或其他人的常量。
The OP wrote
not 'Can it be done?' but to answer the question that wasn't asked before getting to the question that was actually asked:
Since reverse_each is claimed to build an entire array, downto is clearly going to be more efficient. The fact that a language designer could even consider implementing things like that kinda ties into the answer to the actual question as asked.
To answer the question as actually asked...
The reason is because Ruby is an endlessly surprising language. Some surprises are pleasant, but there is a lot of behaviour which is downright broken. Even if some of these following examples are corrected by newer releases, there are plenty of others, and they remain as indictments on the mindset of the original design:
results in "" but
results in
You would probably expect << and push to be the same for appending to arrays, but
You would probably expect 'grep' to behave like its Unix command-line equivalent, but it does === matching not =~, despite its name.
Various methods are unexpectedly aliases for each other, so you have to learn multiple names for the same thing - e.g.
find
anddetect
- even if you do like most developers and only ever use one or the other. Much the same goes forsize
,count
, andlength
, except for classes which define each differently, or don't define one or two at all.Unless someone has implemented something else - like the core method
tap
has been redefined in various automation libraries to press something on the screen. Good luck finding out what's going on, especially if some module required by some other module has monkeyed yet another module to do something undocumented.The environment variable object, ENV does not support 'merge', so you have to write
As a bonus, you can even redefine your or someone else's constants if you change your mind about what they should be.
非常老的问题,但今天我发现了以下方法(之前没有提到过):
使用显式步骤允许 ruby“知道”如何继续到下一个值(而不是使用默认的
succ
)。这种方法的优点是不需要首先实例化完整的数组,而且它仍然具有相当的可读性/紧凑性。
Very old question, but today I found the following approach (which was not mentioned before):
Using an explicit step allows ruby to "know" how to proceed to the next value (instead of using the default
succ
).The advantage of this approach is that no complete array has to be instantiated first, and it still is pretty readable/compact imho.
对我来说,最简单的方法是:
另一种迭代枚举的方法:
As for me the simplest way is:
Another way to iterate for enumeration:
这适用于我的懒惰用例
This worked for my lazy use case