ruby - Ruby - 排列,缺乏输出
问题描述
def permutations(str)
str.split(//).permutation.with_index.to_a.delete_if{|x,i| x[i].eql? x[i+1]}.flatten(1).to_a.delete_if{|x| x.class == Integer}.map{|x| x.join}
end
描述:“在这个 kata 中,您必须创建输入字符串的所有排列并删除重复项(如果存在)。这意味着,您必须以所有可能的顺序打乱输入中的所有字母。”
预期输入/输出:
permutations('aabb') # => ['aabb', 'abab', 'abba', 'baab', 'baba', 'bbaa']
我的输入/输出:
permutations('aabb') # => ["aabb", "abab", "abba"]
所以有一个问题:
'baab', 'baba', 'bbaa'
你有什么想法吗?如果我的代码看起来不清晰,我很抱歉,但我可以设置这样的方法吗?:
str.split(//)
.to_a
.delete_if
.etc
我不确定,但我认为这部分代码有问题,但我无法弄清楚:
.delete_if{|x,i| x[i] == x[i+1]}
解决方案
当遇到这样的问题时,通常是一个好主意
- 将代码分解成更小、更简单的部分
- 编写很多很多很多的测试来编码你所有的假设你认为代码在做什么
- 用笔和纸跟踪代码的执行
- 在调试器中单步执行代码,将其与从笔和纸调试中获得的结果进行比较
我将主要关注#1 和#3。让我们首先重新格式化代码以使其更易于阅读:
str.
split(//).
permutation.
with_index.
to_a.
delete_if { |x, i| x[i].eql? x[i + 1] }.
flatten(1).
to_a.
delete_if { |x| x.class == Integer }.
map { |x| x.join }
现在,让我们分解代码,为每个单独的部分分配一个显示意图的名称。我们要做的第一件事就是split
将String
空字符串分开,换句话说,我们将它分成单个字符。所以,让我们称之为characters
(这基本上只是对现有方法的重新实现String#chars
:
characters = str.split(//)
#=> ['a', 'a', 'b', 'b']
在下一步中,我们将生成此字符数组的所有排列:
permutations = characters.permutation
这会返回一个Enumerator
,然后我们将其转换为Array
:
permutations_array = permutations.to_a
#=> [["a", "a", "b", "b"],
# ["a", "a", "b", "b"],
# ["a", "b", "a", "b"],
# ["a", "b", "b", "a"],
# ["a", "b", "a", "b"],
# ["a", "b", "b", "a"],
# ["a", "a", "b", "b"],
# ["a", "a", "b", "b"],
# ["a", "b", "a", "b"],
# ["a", "b", "b", "a"],
# ["a", "b", "a", "b"],
# ["a", "b", "b", "a"],
# ["b", "a", "a", "b"],
# ["b", "a", "b", "a"],
# ["b", "a", "a", "b"],
# ["b", "a", "b", "a"],
# ["b", "b", "a", "a"],
# ["b", "b", "a", "a"],
# ["b", "a", "a", "b"],
# ["b", "a", "b", "a"],
# ["b", "a", "a", "b"],
# ["b", "a", "b", "a"],
# ["b", "b", "a", "a"],
# ["b", "b", "a", "a"]]
现在我们将每个排列与其索引配对:
permutations_with_index = permutations_array.with_index
这将再次返回给我们一个Enumerator
,我们将其转换为一个Array
:
permutations_with_index_array = permutations_with_index.to_a
#=> [[["a", "a", "b", "b"], 0],
# [["a", "a", "b", "b"], 1],
# [["a", "b", "a", "b"], 2],
# [["a", "b", "b", "a"], 3],
# [["a", "b", "a", "b"], 4],
# [["a", "b", "b", "a"], 5],
# [["a", "a", "b", "b"], 6],
# [["a", "a", "b", "b"], 7],
# [["a", "b", "a", "b"], 8],
# [["a", "b", "b", "a"], 9],
# [["a", "b", "a", "b"], 10],
# [["a", "b", "b", "a"], 11],
# [["b", "a", "a", "b"], 12],
# [["b", "a", "b", "a"], 13],
# [["b", "a", "a", "b"], 14],
# [["b", "a", "b", "a"], 15],
# [["b", "b", "a", "a"], 16],
# [["b", "b", "a", "a"], 17],
# [["b", "a", "a", "b"], 18],
# [["b", "a", "b", "a"], 19],
# [["b", "a", "a", "b"], 20],
# [["b", "a", "b", "a"], 21],
# [["b", "b", "a", "a"], 22],
# [["b", "b", "a", "a"], 23]]
下一步是有趣的地方。
正在进行某种过滤。但目前尚不清楚该过滤究竟在做什么。因此,我们遵循与上述相同的步骤:我们将所有内容分解成小块并单独检查每一块:
mystery = permutations_with_index_array.delete_if do |mystery1, mystery2|
mystery3 = mystery1[mystery2]
mystery4 = mystery1[mystery2 + 1]
mystery5 = mystery3.eql?(mystery4)
end
那么,让我们看看这些碎片是什么。s 数组的每个元素到块。因此, ed的第一个元素是。Array#delete_if
yield
yield
[["a", "a", "b", "b"], 0]
因为我们的块实际上有两个参数,mystery1
并且mystery2
,这个元素(它本身是一个数组)被解构为第一个和第二个元素,这意味着mystery1
是字符数组并且mystery2
是索引。所以,我们已经弄清楚了我们最初的两个谜团:
mystery = permutations_with_index_array.delete_if do |character_array, index|
mystery3 = character_array[index]
mystery4 = character_array[index + 1]
mystery5 = mystery3.eql?(mystery4)
end
现在,让我们仔细看看mystery3
、mystery4
和mystery5
,并使用一个具体的例子。实际上,由于这是一个循环,让我们使用三个具体示例:第一次迭代、中间某处的迭代和最后一次迭代。
在第一次迭代中,character_array
is['a', 'a', 'b', 'b']
和index
is 0
。这意味着mystery3
是 , 的第一个元素character_array
,'a'
并且也是mystery4
, 的第二个元素。character_array
'a'
由于'a'
等于'a'
, mystery5
is true
,这意味着整个块是true
,这意味着整个排列被丢弃。
现在让我们看看第五次迭代。character_array
是['a', 'b', 'a', 'b']
和index
是4
。那就是说mystery3
是第五元素character_array
,但是没有第五元素!请求一个实际上不存在的数组索引返回nil
,因此mystery3
是nil
。并且mystery4
是 的第六个元素character_array
,也就是nil
。
由于nil
等于nil
, mystery5
is true
,这意味着整个块是true
,这意味着整个排列被丢弃。事实上,从这次迭代开始,所有元素都会被丢弃,因为index
只会越来越大。你永远不会得到超过四个排列(在这个例子中)。
更一般地说,你总是会得到至少一个排列,即第str.length - 1
th 个排列,因为在第str.length - 1
th 次迭代中,x[i]
将是一个字符,并且x[i + 1]
将是nil
,它们永远不会相等。而且您最多 str.length - 1
会得到排列,因为从str.length
第 th 次迭代开始,两者x[i]
和x[i + 1]
将永远是nil
,因此永远是相等的。
我将在这里停止查看其余代码,因为我们可以清楚地看到我们已经找到了问题以及问题所在:您将数组的索引与所有排列混淆,而数组是单个排列.
这很容易解决,但是我们会遇到一个不同的问题:不仅你“做错了”,甚至你试图做的事情也是错误的。您试图通过仅查看当前索引和下一个索引来确定重复项。但是,如果两个东西不相邻,它们仍然是重复的!这才是真正Enumerable#uniq
的目的。
推荐阅读
- vue.js - 用 Vue Jest 测试异步功能不起作用
- javascript - React:如何将函数(“插值”)传递给样式化组件的模板
- html - 如何更改日期的颜色?
- javascript - 如何使用 Vue.js 保存待办事项
- tsql - 从一个表中的 2 行中选择到第二个表中包含 2 列或更多列的单行
- css - 如何将我的 mat-table CSS 列宽设置为只占用与数据一样多的空间?
- r - 我的数据框第一行的单个变量不被识别为对象
- reactjs - 当我尝试遵循 ReactJS.org 文档时出错
- google-apps-script - 您无权调用 Ui.showModalDialog 错误,但 Scope 存在,并且对话框仍然显示
- c++ - 如何将打包的参数类型放入元组中?