【问题标题】:How to split a string like the following example in ruby? [closed]如何在 ruby​​ 中拆分如下示例中的字符串? [关闭]
【发布时间】:2016-06-23 02:41:01
【问题描述】:

我想将任意字符串转换为字符串数组。最好用一个例子来解释转换。如果字符串是"8737928" 我想返回以下数组。

#=> ["8737928",
#    "8.737928", "87.37928", "873.7928", "8737.928", "87379.28", "873792.8",
#    "8.7.37928", "8.73.7928", "8.737.928", "8.7379.28", "8.73792.8",
#    "87.3.7928", "87.37.928", "87.379.28", "87.3792.8", "873.7.928",
#    "873.79.28", "873.792.8", "8737.9.28", "8737.92.8", "87379.2.8",
#    "8.7.3.7928", "8.7.37.928", "8.7.379.28", "8.7.3792.8", "8.73.7.928",
#    "8.73.79.28", "8.73.792.8", "8.737.9.28", "8.737.92.8", "8.7379.2.8",
#    "87.3.7.928", "87.3.79.28", "87.3.792.8", "87.37.9.28", "87.37.92.8",
#    "87.379.2.8", "873.7.9.28", "873.7.92.8", "873.79.2.8", "8737.9.2.8",
#    "8.7.3.7.928", "8.7.3.79.28", "8.7.3.792.8", "8.7.37.9.28",  "8.7.37.92.8",
#    "8.7.379.2.8", "8.73.7.9.28", "8.73.7.92.8", "8.73.79.2.8", "8.737.9.2.8",
#    "87.3.7.9.28", "87.3.7.92.8", "87.3.79.2.8", "87.37.9.2.8", "873.7.9.2.8",
#    "8.7.3.7.9.28", "8.7.3.7.92.8", "8.7.3.79.2.8", "8.7.37.9.2.8",
#    "8.73.7.9.2.8", "87.3.7.9.2.8",
#    "8.7.3.7.9.2.8"]

如您所见,字符串中插入了 0 到 6 个 ("8737928".size-1 #=> 6) 小数点,在 1 到 6 之间的每个索引组合处。由于小数点可能会或可能不会跟随除最后一个字符之外的每个字符,该数组包含2**6 #=> 64 元素。

我该怎么做?

【问题讨论】:

  • 你想循环这个还是拆分一个字符串?
  • 我想循环兄弟。我想要这样的所有输出。对不起,因为我不擅长英语
  • 感谢每个人的回答是一个很好的姿态,但我认为这是不明智的。首先,如果每个人都受到感谢,即使是回答不佳的作者也没有多大意义。其次,如果有人给出了一个非常糟糕的答案,而你却不感谢他们,你还不如说,“你的答案很糟糕”。第三,对于阅读 cmets 的每个人来说,这都是(次要的)时间浪费。看看其他问题,你会发现它只是没有完成(而且不是预期的)。
  • 谢谢。我是初学者。我会小心的。
  • 我相信我现在明白了这个问题。问题是您没有显示足够的数组元素以供读者理解您想要的内容。我冒昧地编辑了这个问题。我相信你不介意,我所写的内容准确地描述了你的意图。我已经投票重新提出这个问题。我相应地修改了我的答案。

标签: ruby string split


【解决方案1】:
def doit(str)
  indices = (1..str.size-1).to_a
  indices.each_with_object([str]) { |ndots, combos|
    indices.combination(ndots).each { |sub| combos << dotify(str, sub) } }
end

def dotify(str, indices)
  indices.reverse.each_with_object(str.dup) { |i,s| s.insert(i,'.') }
end

doit("8737928").size
  #=> 64 

doit "8737928"
  #=> ["8737928",
  #    "8.737928", "87.37928", "873.7928", "8737.928", "87379.28", "873792.8",
  #    "8.7.37928", "8.73.7928", "8.737.928", "8.7379.28", "8.73792.8",
  #    "87.3.7928", "87.37.928", "87.379.28", "87.3792.8", "873.7.928",
  #    "873.79.28", "873.792.8", "8737.9.28", "8737.92.8", "87379.2.8",
  #    "8.7.3.7928", "8.7.37.928", "8.7.379.28", "8.7.3792.8", "8.73.7.928",
  #    "8.73.79.28", "8.73.792.8", "8.737.9.28", "8.737.92.8", "8.7379.2.8",
  #    "87.3.7.928", "87.3.79.28", "87.3.792.8", "87.37.9.28", "87.37.92.8",
  #    "87.379.2.8", "873.7.9.28", "873.7.92.8", "873.79.2.8", "8737.9.2.8",
  #    "8.7.3.7.928", "8.7.3.79.28", "8.7.3.792.8", "8.7.37.9.28",  "8.7.37.92.8",
  #    "8.7.379.2.8", "8.73.7.9.28", "8.73.7.92.8", "8.73.79.2.8", "8.737.9.2.8",
  #    "87.3.7.9.28", "87.3.7.92.8", "87.3.79.2.8", "87.37.9.2.8", "873.7.9.2.8",
  #    "8.7.3.7.9.28", "8.7.3.7.92.8", "8.7.3.79.2.8", "8.7.37.9.2.8",
  #    "8.73.7.9.2.8", "87.3.7.9.2.8",
  #    "8.7.3.7.9.2.8"] 

注意:

dotify("8737928", [1,3,5])
  #=> "8.73.79.28"     

【讨论】:

  • 非常感谢
  • 我已经修改了我的答案,以反映我目前对该问题的理解。
【解决方案2】:

解决方案 1

经过进一步思考(请参阅下面的原始解决方案,可能是不正确的解决方案),看起来 OP 真正想要做的是在字符串中每个可能的位置组合处插入点。这是一个真正做到这一点的方法:

def splits(str, prefix="")
  c = str.size - 1
  (0..c).flat_map do |m|
    (0...c).to_a.combination(m).map do |n|
      n.each_with_object(str.dup) {|i,s| s.insert(c-i, ?.) }
    end
  end
end

puts splits("8737928")
# => 8737928
#    873792.8
#    87379.28
#    87379.2.8
#    8737.928
#    8737.92.8
#    8737.9.28
#    8737.9.2.8
#    873.7928
#    873.792.8
#    873.79.28
#    873.79.2.8
#    873.7.928
#    873.7.92.8
#    873.7.9.28
#    873.7.9.2.8
#    87.37928
#    87.3792.8
#    87.379.28
#    87.379.2.8
#    87.37.928
#    87.37.92.8
#    87.37.9.28
#    87.37.9.2.8
#    87.3.7928
#    87.3.792.8
#    87.3.79.28
#    87.3.79.2.8
#    87.3.7.928
#    87.3.7.92.8
#    87.3.7.9.28
#    87.3.7.9.2.8
#    8.737928
#    8.73792.8
#    8.7379.28
#    8.7379.2.8
#    8.737.928
#    8.737.92.8
#    8.737.9.28
#    8.737.9.2.8
#    8.73.7928
#    8.73.792.8
#    8.73.79.28
#    8.73.79.2.8
#    8.73.7.928
#    8.73.7.92.8
#    8.73.7.9.28
#    8.73.7.9.2.8
#    8.7.37928
#    8.7.3792.8
#    8.7.379.28
#    8.7.379.2.8
#    8.7.37.928
#    8.7.37.92.8
#    8.7.37.9.28
#    8.7.37.9.2.8
#    8.7.3.7928
#    8.7.3.792.8
#    8.7.3.79.28
#    8.7.3.79.2.8
#    8.7.3.7.928
#    8.7.3.7.92.8
#    8.7.3.7.9.28
#    8.7.3.7.9.2.8

解决方案 2

然而,虽然@EliSadoff 的解决方案没有被普遍化,但我确实喜欢他的“一个句点可以是一个布尔决定的每个点的想法”。如果我们考虑在字符串中可以插入句点的位置作为二进制数 m 中的位,其长度与字符串减一的长度相同(基数为 2),我们可以简单地从 0 开始迭代到 2(c-1)-1 (其中 c 是字符串的长度)以获取每个可能的此类数字。例如,如果我们的字符串是"abcd" (c = 4),那么我们可以从 0 迭代到 7 (2(4-1)-1) 来找到每个时期的位置:

 m₁₀ | m₂ 4 2 1 |   4   2   1   | result
─────┼────┴─┴─┴─┼───┴───┴───┴───┼─────────
   0 │    0 0 0 │ a   b   c   d │ abcd
   1 │    0 0 1 │ a   b   c • d | abc.d
   2 │    0 1 0 │ a   b • c   d | ab.cd
   3 │    0 1 1 │ a   b • c • d | ab.c.d
   4 │    1 0 0 │ a • b   c   d | a.bcd
   5 │    1 0 1 │ a • b   c • d | a.bc.d
   6 │    1 1 0 │ a • b • c   d | a.b.cd
   7 │    1 1 1 │ a • b • c • d | a.b.c.d

唯一缺少的部分是根据第二列中的位插入句点。这很简单:要确定是否需要在位置 n 处插入句点,我们测试 m 中的第 n 位是否为 1 . 为此,我们可以使用按位运算 m & (1 ≪ n)。

把它们放在一起,我们得到以下结果:

def splits2(str)
  c = str.size - 1
  (0...2**c).map do |m|
    0.upto(c).with_object(str.dup) do |i,s|
      s.insert(c-i, ?.) if m & (1 << i) > 0
    end
  end
end

解决方案 3

只是为了好玩,这是另一种解决方案,它也使用二进制数方法,但方式不同。我将把它作为练习留给读者来弄清楚它是如何工作的:

def splits3(str)
  c = str.size - 1
  (0...2**c).map do |m|
    dots = ("%*b" % [c,m]).each_char.map(&{?1=>?.})
    str.each_char.zip(dots).join
  end
end

原方案

类似于@CarySwoveland 的解决方案,但我认为更简单:

def splits(str, pfx="")
  return [] if str.empty?
  (1...str.size).map {|i| pfx + str.dup.insert(i, ?.) } +
    splits(str[1..-1], "#{pfx}#{str[0]}.")
end

p splits("8737928")
# => [ "8.737928", "87.37928", "873.7928", "8737.928", "87379.28", "873792.8",
#      "8.7.37928", "8.73.7928", "8.737.928", "8.7379.28", "8.73792.8",
#      "8.7.3.7928", "8.7.37.928", "8.7.379.28", "8.7.3792.8",
#      "8.7.3.7.928", "8.7.3.79.28", "8.7.3.792.8",
#      "8.7.3.7.9.28", "8.7.3.7.92.8",
#      "8.7.3.7.9.2.8"
#     ]

【讨论】:

  • 非常感谢
  • 是的,一个改进。
  • 我已经编辑了我的答案以包含三个我认为符合 OP 意图的解决方案。
【解决方案3】:

要求不明确,我得出的结果与 Cary 和 Jordan 的结果不同:

 def dot_it(prefix, suffix = nil)
   return dot_it(prefix[0], prefix[1..-1]) if suffix.nil? # first call

   (1...suffix.length).flat_map do |i|
     sp, ss = "#{prefix}.#{suffix[0...i]}", suffix[i..-1]
     ["#{sp}.#{ss}", dot_it(sp, ss)].flatten.compact
   end
end
dot_it("8737928")
#⇒ ["8.7.37928", "8.7.3.7928", "8.7.3.7.928", "8.7.3.7.9.28",
#   "8.7.3.7.9.2.8", "8.7.3.7.92.8", "8.7.3.79.28", "8.7.3.79.2.8",
#   "8.7.3.792.8", "8.7.37.928", "8.7.37.9.28", "8.7.37.9.2.8",
#   "8.7.37.92.8", "8.7.379.28", "8.7.379.2.8", "8.7.3792.8",
#   "8.73.7928", "8.73.7.928", "8.73.7.9.28", "8.73.7.9.2.8",
#   "8.73.7.92.8", "8.73.79.28", "8.73.79.2.8", "8.73.792.8",
#   "8.737.928", "8.737.9.28", "8.737.9.2.8", "8.737.92.8",
#   "8.7379.28", "8.7379.2.8", "8.73792.8"]

我的方法给出:

dot_it("8737928").count
#⇒ 31

虽然以上两个答案都给出了 21 个结果。谁是对的?

【讨论】:

  • 非常感谢
  • 代码加6,方法名加4。
  • 但是我的朋友说这个问题给出了 64 个结果。我正在尝试这样做。
  • 很高兴查看这 64 条结果。
  • @CarySwoveland 我会说方法名称比解决方案更优雅;您应该考虑将评估交换为 6 用于方法名称,2 用于在 cmets 中开始有趣的聊天,2 用于代码。
猜你喜欢
  • 1970-01-01
  • 2011-04-25
  • 2011-04-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-21
相关资源
最近更新 更多