【问题标题】:Sorting strings in array without sorting function - Ruby在没有排序功能的情况下对数组中的字符串进行排序 - Ruby
【发布时间】:2019-03-23 08:37:54
【问题描述】:

我正在尝试在 Ruby 中创建一个没有排序功能的排序算法。我基于插入排序的想法。这个想法是该函数检查每两个单词的第 n 个值是否相同,如果是,则 n 增加 1 直到一个值大于另一个值。在这种情况下,单词可能会被切换。但是,我的功能一直冻结。任何想法为什么?

words = ["my","favorite","animal", "are", "the", "elephant", "and", "the", "antelope", "and", "the", "favela"]

#Convert all letters into numbers. Output individual words as arrays.
converted_words = words.map(&:chars).map { |letters| letters.map { |letter| letter.to_i 36 } }
puts converted_words.to_s

i = 1
x = 0
while i < converted_words.length
  if converted_words[i][x] == converted_words[i-1][x]
    x = x + 1
  else
    if converted_words[i][x] < converted_words[i-1][x]
      converted_words[i], converted_words[i-1] = converted_words[i-1], converted_words[i]
      i = 0
      x = 0
    else
      i = i + 1
    end
  end
end
puts converted_words.to_s

【问题讨论】:

  • 能否请您对输出进行示例?
  • 如果不增加i,函数将永远循环。
  • @Benisburgers:你确定吗?
  • 正如@YvesDaoust 指出的那样,在某些情况下您不会增加i。而且,是的,x 有问题。无论何时更改i,都必须重置x。此外,如果您设置i=0,然后尝试访问converted_words[i-1][0],您将离开阵列的后端。而且您没有终止条件来停止在单词的末尾。您需要启动调试器并单步执行您的代码,以查看它在哪里偏离了轨道。
  • 另外,这里有一种效率极低的排序算法,与插入排序几乎没有相似之处。如果我正确地阅读了意图,你会向前搜索,直到你找到一个不合适的项目。您交换项目,然后从头开始。因此,如果您想将一个项目从数组的末尾移动到开头,则需要 n 遍历数组。这构成了一个 O(n^3) 算法。给定一个足够大的数组,它实际上不会进入无限循环;它看起来就像一个无限循环。

标签: ruby algorithm sorting insertion-sort


【解决方案1】:

您的代码不会“冻结”;运行它会引发此异常:

NoMethodError (undefined method '<' for nil:NilClass)

在行中:

if converted_words[i][x] < converted_words[i-1][x]

我们立即发现了问题,但原因尚不清楚。方法&lt; 的接收者是converted_words[i][x]。由于错误消息说nil 没有方法&lt;,我们推断converted_words[i][x]nil1 这意味着索引超出范围(示例超出范围的索引是[1,2][412] #=&gt; nil[1,2][-3] #=&gt; nil)。如果i 超出范围,则表达式将缩减为nil[x] &lt; ...,这将引发nil 没有方法NilClass#\[\]] 的异常。这不是我们的异常消息,因此我们得出结论,x 必须超出范围。

要了解为什么会发生这种情况,假设:

words = ["a", "ab"]

然后

converted_words =
  words.map(&:chars).map { |letters| letters.map { |letter| letter.to_i 36 } }
  #=> [[10], [10, 11]] 
i = 1
x = 0
while i < converted_words.length
  #=> while 1 < 2 => while true, so enter the loop
if converted_words[i][x] == converted_words[i-1][x]
  #=> if converted_words[1][0] == converted_words[0][0] => if 10 == 10 => true

所以执行

x = x + 1
  #=> x = 0 + 1 => 1

并尝试重复循环。

while i < converted_words.length
  #=> while 1 < 2 => while true, so repeat the loop
if converted_words[i][x] == converted_words[i-1][x]
  #=> if converted_words[1][1] == converted_words[0][1] => if 11 == nil => false

所以执行 (else)。

if converted_words[i][x] < converted_words[i-1][x]
  #=> converted_words[0][1] < converted_words[-1][1] => if nil < 11
  #=> NoMethodError (undefined method '<' for nil:NilClass)

错误消息包含有价值的信息。仔细研究它们!

1 错误信息“nil has no method &lt;”在这里等价于“NilClass has no instance method &lt;”。

【讨论】:

  • 非常感谢。这就是我一直在寻找的。感谢您的辛勤工作。但是您是如何访问错误消息的呢?我的终端只是停下来,什么也不做。
  • 我刚刚将您的代码复制并粘贴到 IRB 中并运行它。 (Ruby v2.5.1,但我认为不是版本问题。)
【解决方案2】:

我相信我已经解决了这个问题。谢谢你的帮助。

我重新排序了我的算法:首先检查if converted_words[i][x] &lt; converted_words[i-1][x],然后然后检查if converted_words[i][x] == converted_words[i-1][x]

我还需要检查是否if converted_words[i][x] != nil &amp;&amp; converted_words[i-1][x] != nil,以避免出现 NoMethodError(感谢 Cary Swoveland)。 最后我把这两种算法结合起来。

我还意识到我不需要将字母转换为数字,因为 ruby​​ 知道哪些字母更大。因此,我将字符保留为字母。

我知道代码不是很有效。如果您对如何改进或简化算法有任何建议,我很乐意听到。

代码如下:

words = ["my","favorite","animals", "are", "the", "elephant", "and", "the", "antelope", "and", "the", "favela"]
puts words.to_s
#Convert all letters into numbers. Output individual words as arrays.
ordered_words = words.map(&:chars).map { |letters| letters.map { |letter| letter } }

i = 1
x = 0
while i < ordered_words.length
  if ordered_words[i][x] != nil && ordered_words[i-1][x] != nil
    if ordered_words[i][x] < ordered_words[i-1][x]
      ordered_words[i], ordered_words[i-1] = ordered_words[i-1], ordered_words[i]
      i = 1
      x = 0
    else
      if ordered_words[i][x] == ordered_words[i-1][x]
        x = x + 1
      else
        i = i + 1
        x = 0
      end
    end
  else
    if ordered_words[i][x] == nil && ordered_words[i-1][x] == nil
      i = i + 1
      x = 0
    else
      if ordered_words[i][x] == nil
        ordered_words[i], ordered_words[i-1] = ordered_words[i-1], ordered_words[i]
        i = 1
        x = 0
      else
        i = i + 1
        x = 0
      end
    end
  end
end

joined_words = []
ordered_words.each do |word|
  joined_words.push(word.join)
end
puts joined_words.to_s

【讨论】:

    猜你喜欢
    • 2020-02-25
    • 2014-11-13
    • 2023-02-18
    • 2021-11-27
    • 1970-01-01
    • 2013-05-14
    • 1970-01-01
    • 2017-10-09
    • 1970-01-01
    相关资源
    最近更新 更多