正则表达式检测数字序列

时间:2013-10-23 19:16:12

标签: ruby regex

我提出了以下正则表达式,作为检查由连续序列中的一组数字按升序或降序组成的数据的方法。

明显的约束:字符串长度在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

注意使它更紧凑或更漂亮?

7 个答案:

答案 0 :(得分: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

对于super_string的想法以及使用str [s]的Tin Man来说,信用是真的。

答案 1 :(得分: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

注意:如果给定的字符串具有n&gt;的(10n + 1)个数字,则返回true。如果除了last之外的所有数字都按顺序排列为0如果不是必需的输出,可以修改它以返回false。

答案 2 :(得分: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

答案 3 :(得分:2)

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

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

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

我将解决方案改为:

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

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

我在此撤回我提出的赌注。

答案 4 :(得分: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";
}                                    

答案 5 :(得分: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

答案 6 :(得分: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!等简单检查来实现。

编辑:无需支票,如果套装有重复的数字,我们的主要要求也会失败。