学习红宝石。需要返回一个长度为n的数组,其中给定数字x,然后是前一个数字的平方
这就是问题
完成返回一个长度为 n 的数组的函数,从给定的数字 x 和前一个数字的平方开始。如果 n 为负数或零,则返回空数组/列表。
Examples
2, 5 --> [2, 4, 16, 256, 65536]
3, 3 --> [3, 9, 81]
对于我一直在学习的东西来说似乎很简单。
我已经完成了大部分代码,因为它完成了示例:
def squares(x, n)
array = [x]
i = 1
while i < n
array << x *= x
i += 1
end
return array
end
PASS Test.assert_equals(squares(2,5),[2,4,16,256,65536]);
PASS Test.assert_equals(squares(3,3),[3,9,81]);
PASS Test.assert_equals(squares(5,3),[5,25,625]);
PASS Test.assert_equals(squares(10,4),[10,100,10000,100000000]);
我遇到的麻烦是,该问题还要求 n 为正整数,如果不是,则返回一个空数组:
如果n为负数或零,则返回空数组/列表。
我很难弄清楚如何正确地解决这个问题。我尝试了几种不同的方法,但没有成功。这是我的一次尝试的例子,我认为我走在正确的轨道上:
def squares(x, n)
array = [x]
arr = []
i = 1
if n < 0
return arr
elsif i < n
array << x *= x
end
i += 1
end
end
This is the problem
Complete the function that returns an array of length n, starting with the given number x and the squares of the previous number. If n is negative or zero, return an empty array/list.
Examples
2, 5 --> [2, 4, 16, 256, 65536]
3, 3 --> [3, 9, 81]
Seems easy enough for what Ive been learning.
Ive completed the code for the most part as it completes the examples:
def squares(x, n)
array = [x]
i = 1
while i < n
array << x *= x
i += 1
end
return array
end
PASS Test.assert_equals(squares(2,5),[2,4,16,256,65536]);
PASS Test.assert_equals(squares(3,3),[3,9,81]);
PASS Test.assert_equals(squares(5,3),[5,25,625]);
PASS Test.assert_equals(squares(10,4),[10,100,10000,100000000]);
The trouble Im having is, the problem also calls for n to be a positive integer, and if not, return an empty array:
If n is negative or zero, return an empty array/list.
Im having difficulty figuring out how to correctly go about this. Ive tried several different ways, with no success. This is an example of one of my attempts where I thought I was on the right track:
def squares(x, n)
array = [x]
arr = []
i = 1
if n < 0
return arr
elsif i < n
array << x *= x
end
i += 1
end
end
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
一种方法是创建一个枚举器,然后获取所需数量的元素:
请参阅 枚举器::产生。
One way is to create an enumerator and then take the required number of elements:
See Enumerator::produce.
只需在方法顶部添加以下行即可解决此问题:
尽管还有许多其他方法可以做到这一点。例如:
这依赖于以下事实:如果
n <= m
,m..n
形式的 Ruby 范围不会产生任何元素。Just adding the following line at the top of your method will solve it:
Though there are many other ways to do this. For example:
This relies on the fact that Ruby ranges of form
m..n
produce no elements ifn <= m
.首先我想说这个问题很愚蠢。不是你的问题,我说的是问题陈述:
它有几个问题。
Array
。在编程中,您应该始终努力使返回值尽可能通用,以便您的子例程可以在尽可能多的不同上下文中重用。如果您的子例程返回一个Array
并且n
非常大,那么Array
将使用大量内存。但是,例如,如果有人只想迭代所有这些值,则不需要Array
。返回通常所说的流或者Ruby所说的枚举器
。如果客户端确实需要Array
,则可以轻松地将Enumerator
转换为Array
。n
的要求。返回无限数量的元素并让客户端选择他们需要的数量会更有意义。例如,如果他们不知道元素的数量怎么办?如果他们想要将这些数字相加直到达到某个阈值怎么办?根据问题陈述的要求,他们必须猜测需要多少个数字才能达到该阈值,而如果你给他们一个无限流,他们可以迭代该流,直到找到答案。raise
更有意义 一个Exception
,更准确地说是一个 <一个href="https://ruby-doc.org/core/ArgumentError.html" rel="nofollow noreferrer">ArgumentError
表示负n
返回空结果。空结果对于n == 0
有意义,但对于负n
则不一定。]因此,简而言之,问题陈述的措辞方式会导致糟糕的 Ruby 术语(函数、列表)和糟糕的编程实践(返回值太具体,没有正确地发出错误信号)。
无论如何,回到你的问题。
正如其他一些答案中提到的,您几乎可以直接将该语句翻译为 Ruby 代码:
现在我们可以稍微优化一下。首先,如果您的条件表达式没有
else
分支,并且then
分支中只有一个表达式,则可以使用所谓的修饰符 形式:其次,“负数或零”只是“非正数”的另一种表达方式,这使我们能够简化条件:
我们可以通过使用
条件表达式的倒置形式来使其更具可读性除非
:这满足问题陈述中的要求,但是,正如我提到的,我个人认为传递负长度应该是一个错误,所以我可能宁愿写这样的东西:
不过,正如我上面提到的,问题陈述的方式迫使你写该代码不是您在现实世界中实际编写的代码。在现实世界中,您会将问题分解为各种正交组件,并确保每个组件都可以单独使用。
原因是“特定长度的正方形序列”是一个非常具体的问题,这使得其他人不太可能遇到完全相同的问题,因此不太可能您的代码可以重复使用。
我会将此问题至少分解为以下子问题:
如果您已经解决了这三个子问题,则可以使用以下解决方案来解决特定问题:(1) 生成 (3) 个正方形的无限流,以及 (2) 仅采用前 n 个元素。
这种方法有什么优点?我看到两个主要问题:
事实上,我已经向您隐藏了最重要的好处:还记得我多次说过,让问题变得更普遍有助于使解决方案更可重用,从而使解决方案在更多情况下变得有用吗?好吧,事实证明,#1 和 #2 非常通用,并且在很多上下文中都很有用,以至于它们已经是为我们写的!! #1 和 #2 的解决方案是 Ruby 核心库的一部分,因此我们甚至不需要自己编写它们。
您可以使用方法 生成无限的
Enumerator
Enumerator::product
,这是 展开又名变形。您可以从Enumerable
使用Enumerable#take
。所以,我们剩下要解决的就是如何对一个数进行平方,这很简单。您还会注意到,当您传递
0
作为元素数量并raise
时,Enumerable#take
返回一个空的Array
> 当您传递负数时,会出现ArgumentError
,因此通过分解我们的问题并将子问题 #2 的解决方案委托给Enumerable#take
,我们也会得到该错误,并且我们想要的边缘情况行为免费。。您已经可以在 Cary Swoveland 的回答中看到生成的代码是什么样子,所以我在这里不再重复。相反,我想展示我一开始的意思,当时我说返回无限的正方形流会更有用,因为客户可以应用自己的标准来确定要采用的元素数量。记住我提出的问题:
如果我们这样编写我们的方法:
那么客户端所要做的就是将
Enumerable#take
(这允许它们获取特定数量的元素)替换为Enumerable#take_while
(允许它们在特定条件下获取元素是 :因此,总而言之,将问题分解为子问题并概括这些子问题总是一个好主意,因为分解问题使问题变得更简单,而概括则使其更有可能在以下方面有用 其他情况,并且更有可能已经被其他人解决了。特别是,您应该始终将 I/O 与计算分开,并尝试将生成数据、转换数据、过滤数据和减少数据彼此分开。
First off, I would like to say that the question is stupid. Not your question, I am talking about the problem statement:
There are several things wrong with it.
Array
. In programming, you should always strive to make your return values as generic as possible, so that your subroutine can be re-used in as many different contexts as possible. If your subroutine returns anArray
andn
is very large, then theArray
will use a lot of memory. However, if someone just wants to iterate over all of those values, for example, there is no need for anArray
. It would make much more sense to return what is commonly called a stream or what Ruby calls anEnumerator
. A client can easily convert anEnumerator
to anArray
if they really need anArray
.n
. It would make much more sense to return an infinite number of elements and let the client pick the number they need. For example, what if they don't know the number of elements? What if they want to sum those numbers until they reach a certain threshold? With the requirement from the problem statement, they would have to guess how many numbers they need in order to reach that threshold, whereas if you give them an infinite stream, they can just iterate the stream until they have found their answer.raise
anException
, more precisely anArgumentError
for a negativen
instead of returning an empty result. The empty result makes sense forn == 0
, but not necessarily for negativen
.]So, in short, the way the problem statement is worded promotes bad Ruby terminology (function, list) and bad programming practices (too specific return value, not properly signaling errors).
Anyway, back to your question.
As mentioned in some of the other answers, you can almost literally translate that statement to Ruby code:
Now we can optimize this a little bit. First off, if you have a conditional expression with no
else
branch and only a single expression in thethen
branch, you can use the so-called modifier form:Secondly, "negative or zero" is just a different way of saying "not positive", which allows us to simplify the condition:
We can make this more readable by using the inverted form of the conditional expression with
unless
:This satisfies the requirement in the problem statement, however, as I mentioned, I personally think passing a negative length should be an error, so I would probably rather write something like this:
As I mentioned above, though, the way the problem statement forces you to write the code is not how you would actually write it in the real world. In the real world, you would decompose the problem into various orthogonal components, and make sure that each of those components can also be used separately.
The reason is that "a sequence of squares of a specific length" is a very specific problem, which it makes it very unlikely that someone else is going to have the exact same problem, and thus makes it unlikely that your code can be re-used.
I would decompose this problem into at least these subproblems:
If you have solved these three subproblems, you can solve the specific problem using the solutions by (1) producing an infinite stream of (3) squares and (2) taking only the first
n
elements.What are the advantages of this approach? I see two major ones:
In fact, I have hidden the most important benefit from you: remember how I said multiple times that making the problem more general helps make the solution more reusable, so that the solution becomes useful in more contexts? Well, it turns out that #1 and #2 are so general and so useful in so many contexts, that they have already been written for us! The solutions to #1 and #2 are part of the Ruby core library, so we don't even need to write them ourselves.
You can produce an infinite
Enumerator
using the methodEnumerator::produce
, which is Ruby's name for an unfold aka Anamorphism. And you can take a specified number of elements from anEnumerable
usingEnumerable#take
. So, all that's left for us to solve here is how to square a number, which is trivial.You will also note that
Enumerable#take
returns an emptyArray
when you pass0
as the number of elements andraise
s anArgumentError
when you pass a negative number, so by decomposing our problem and delegating the solution of sub-problem #2 toEnumerable#take
we also get the error and edge case behavior we want for free.You can already see in Cary Swoveland's answer what the resulting code looks like, so I will not repeat it here. Rather, I want to show what I meant at the very beginning when I said that returning an infinite stream of squares would be more useful because the client could then apply their own criterion for how many elements to take. Remember the problem I posed:
If we write our method like this:
Then all the client has to do is to replace
Enumerable#take
(which allows them to take a specific number of elements) withEnumerable#take_while
(which allows them to take elements while a specific condition is met), and they can write:So, in summary, it is always a good idea to break down problems into subproblems and generalize those subproblems, because breaking the problem down makes it simpler, and generalizing makes it both more likely to be useful in other contexts, and more likely to already have been solved by someone else. In particular, you should always separate I/O from computation, and try to separate generating data, transforming data, filtering data, and reducing data from each other.