3

我想出了以下正则表达式来检查仅由一组按升序或降序排列的连续数字组成的数据。

明显的限制:字符串的长度在 2 到 10 位之间,因为一位不是序列,并且必须重复超过十位。其他代码将确保输入仅包含数字。(例如 /\A\d{2,}\z/

例子:

  • '012','9876'并且'56'应该匹配
  • '7', '013','6554''09'应该

我认为这可以完成工作:

/(?:\A(?:0(?=1)|1(?=2)|2(?=3)|3(?=4)|4(?=5)|5(?=6)|6(?=7)|7(?=8)|8(?=9)|\d(?!\d)){2,}\z)|
 (?:\A(?:1(?=0)|2(?=1)|3(?=2)|4(?=3)|5(?=4)|6(?=5)|7(?=6)|8(?=7)|9(?=8)|\d(?!\d)){2,}\z)/x

问题来了:你能找到一种更简洁或更漂亮的方式来用兼容 Ruby 的正则表达式来表达它吗?

显然,几个嵌套循环将是同一问题的非正则表达式解决方案。

if num.length > 1
  [Proc.new { |n| n + 1 }, Proc.new { |n| n - 1 }].each do |p|
    is_sequential = true
    (0..num.length - 2).each do |i|
      if p.call(num[i].ord) != num[i + 1].ord
        is_sequential = false
        break
      end
    end
    return 'Number is sequential' if is_sequential
  end
end

想让它更紧或更漂亮吗?

4

7 回答 7

3
def isseq( s )
  1 < s.length && !!( '0123456789'[s] || '9876543210'[s] )
end

[ '012', '9876', '56', '7', '013', '6554', '09' ].each do |test|
  puts "#{test} #{isseq(test)}"
end

输出:

012 true
9876 true
56 true
7 false
013 false
6554 false
09 false

感谢 tihom 的 super_string 想法和使用 str[s] 的 Tin Man。

于 2013-10-23T20:35:25.710 回答
2
super_string = "0123456789"

'012'.scan(/\d{2,10}/).
map{|x| super_string.include?(x) || super_string.reverse.include?(x) }.
uniq == [true]
#=> true

'013'.scan(/\d{2,10}/).map{|x| super_string.include?(x) || super_string.reverse.include?(x) }.uniq == [true]
#=> false

注意:如果给定字符串具有 (10n+1) 个数字且 n > 0 且除最后一个数字之外的所有数字都按顺序排列,则返回 true。如果这不是所需的输出,则可以将其修改为返回 false。

于 2013-10-23T20:03:04.540 回答
2

对于给定的初始数字和字符串长度,只有两个可能的有效字符串。只需生成它们并进行比较。

def sequential_num(num)

  return false if num =~ /\D/ or num.length <= 1

  initial = num[0]
  range = (num.length - 1)

  final = (initial.ord + range).chr
  return true if final <= '9' and num == (initial..final).to_a.join

  final = (initial.ord - range).chr
  return true if final >= '0' and num == (final..initial).to_a.reverse.join

  return false
end


%w/ 012 9876 56 7 013 6554 09 /.each do |num|
  puts '%-4s %s' % [ num, sequential_num(num) ? 'match' : 'no match' ]
end

输出

012  match
9876 match
56   match
7    no match
013  no match
6554 no match
09   no match
于 2013-10-23T20:33:30.300 回答
2

这是在 elixir 中尝试非正则表达式功能解决方案:

#!/usr/bin/env elixir

defmodule Seq do

  def is_seq(list) when match([x, y | t], list), do: is_desc(list) || is_asc(list)
  def is_seq(_), do: false

  def is_desc([first, second | tail]), do: second == first - 1 && is_desc [second | tail]
  def is_desc(_), do: true

  def is_asc([first, second | tail]), do: second == first + 1 && is_asc [second | tail]
  def is_asc(_), do: true

  def test() do
    ['012', '9876', '56', '7', '013', '6554', '09', '012345678', '01234678'] |> Enum.map &is_seq/1
  end
end

Seq.test |> IO.inspect
于 2013-11-05T16:36:43.023 回答
2

编辑:我误读了这个问题。最初我有:

10美元说你要拍你的额头。

s = str.split('')
sorted = s.sort
sorted == s || sorted == s.reverse

我将解决方案更改为:

  s = "0123456789"
  s.include?(str) || s.reverse.include?(str)

但后来看到@Matt 已经给出了这个解决方案。

我在此撤回我的赌注。

于 2013-10-23T20:49:00.103 回答
2

Dan,有时需要考虑在进行模式匹配时将内容放在哪里。这里有趣的是,'0123456789 9876543210' 是一个包含所有可能正确答案的字符串。一旦我们将输入验证为所有数字,长度>1,就可以使用模式匹配来查看数据是否出现在解决方案的范围内。我的 Perl 说得很好,所以我会坚持下去。这是一般测试:

验证数字也是如此$nstring,长度> 1。

print "$nstring matches!\n" if '0123456789 9876543210' =~ /$nstring/;

或者一个简单的程序审计一个合适的字符串然后测试:

while (<STDIN>) 
{
    chomp;
    next if not /^[0-9]{2,}$/;
    $nstring=$_;
    $matches='0123456789 9876543210' =~ /$nstring/?"matches":"does not match";
    print "$nstring $matches!\n";
}                                    
于 2013-10-28T18:20:54.570 回答
1

我认为正则表达式对于这项工作来说不是一个好主意。我们的问题可以归结为“每个数字之间的差值的绝对值应该只有一个”,简单地说:

irb(main):052:0> s
=> "2345678"
irb(main):053:0> pairs = s.chars.zip(s[1..10].chars).select {|i| i[0] and i[1]}
=> [["2", "3"], ["3", "4"], ["4", "5"], ["5", "6"], ["6", "7"], ["7", "8"]]
irb(main):054:0> pairs.all? {|i| (i[0].to_i - i[1].to_i).abs == 1}
=> true

其余的要求可以通过简单的检查来实现,例如"23647".chars.uniq!.

编辑:不需要检查,如果集合有重复的数字,我们的主要要求也失败了。

于 2013-10-23T19:33:52.987 回答