Tạo các biểu thức chính quy để khớp các số tự nhiên giữa `m` và` n`


8

Loại biểu thức chính quy là PCRE.

Viết chương trình xuất ra một PCRE hợp lệ sao cho nó khớp với tất cả các số tự nhiên giữa mnkhông khớp với bất kỳ thứ gì khác. Không có số không hàng đầu được phép.

Ví dụ, let mnbe 1234321, sau đó chương trình có thể xuất ra 1(2[3-9]|[3-9]\d)|[2-9]\d\d|[123]\d\d\d|4([012]\d\d|3([01]\d|2[01])).

Điều này phù hợp với chuỗi chính xác, vì vậy ^$neo là ẩn.

Người ta nên cố gắng cân bằng hai:

  1. Các biểu thức chính quy nên có kích thước hợp lý.

  2. Mã phải ngắn.

Hãy tối ưu hóa cho

code length in characters + 2 *regular expression length for input 123456 and 7654321

Lưu ý bên lề: Thật thú vị nếu chúng ta có thể chứng minh biểu thức chính quy PCRE ngắn nhất có kích thước O (log n log log n) hoặc một cái gì đó.


1
Bạn có thể xác định các tiêu chí chiến thắng? Có thể một cái gì đó như re_size*5 + prog_size(nhỏ hơn = tốt hơn), trong đó re_sizetối đa cho mvà tối đa n5 chữ số. Có nhiều cách khác để làm điều đó - điều quan trọng là chúng ta có thể chấm điểm các câu trả lời.
ugoren

"Viết chương trình xuất ra một PCRE hợp lệ sao cho nó khớp với tất cả các số tự nhiên giữa m và n" Có lẽ "và không khớp với tất cả các đầu vào khác", phải không? Lest một số ass thông minh cung cấp print .*trong một số ngôn ngữ.
dmckee --- ex-moderator mèo con

Sẽ vui hơn với các số âm :-D
JB

if(min == 123456 && max == 7654321){print_hardcoded_regex}else{enumerate_range_and_join}
John Dvorak

Câu trả lời:


7

Perl, điểm số 455

191 ký tự, chiều dài 132 regex

sub b{my$a=shift;$_=(@_>0&&$a.b(@_).($a-->0&&'|')).($a>=0&&($a>1
?"[0-$a]":$a));/\|/?"($_)":$_}sub a{b(@a=split//,<>-1+$x++).(@a>
1&&"|.{1,$#a}")}print'(?!0|('.a.')$)(?=('.a.'$))^\d{1,'.@a.'}$'

Đầu vào: 123456, 7654321

(?!0|((1(2(3(4(5[0-5]|[0-4])|[0-3])|[0-2])|1)|0)|.{1,5})$)(?=((7(6(5(4(3(21|1)|[0-2])|[0-3])|[0-4])|[0-5])|[0-6])|.{1,6}$))^\d{1,7}$

Cập nhật: Tôi đã có thể đơn giản hóa hơn nữa khi tôi nhận ra rằng hầu hết các mẫu kết thúc với những thứ như \d{3}. Chúng không làm gì khác hơn là thực thi độ dài chuỗi - và làm như vậy rất lặp đi lặp lại, vì chúng xảy ra trên mọi thuật ngữ. Tôi đã loại bỏ điều này bằng cách sử dụng một giao diện khác để thực thi điều kiện "nhỏ hơn", chỉ kiểm tra xem: 1) phần đầu tiên của số không vượt quá đầu vào hoặc 2) số có ít chữ số hơn đầu vào. Sau đó, regex chính chỉ xác minh rằng nó không quá nhiều chữ số.

Tôi cũng kết hợp ý tưởng của Peter Taylor về cái nhìn tiêu cực để kiểm tra điều kiện "lớn hơn".

Chìa khóa để đơn giản hóa vấn đề này (ít nhất là đối với tôi) là chia nhỏ regex thành hai: nhìn về phía trước đảm bảo số lượng không nhỏ hơn mức tối thiểu, sau đó phần chính của biểu thức chính kiểm tra rằng nó không lớn hơn tối đa. Đó là một chút ngớ ngẩn cho đầu vào nhỏ, nhưng nó không quá tệ cho đầu vào lớn.

Lưu ý: 0|lúc đầu là để loại trừ bất cứ thứ gì bắt đầu bằng số 0 khỏi khớp. Điều này là bắt buộc vì tính chất đệ quy của hàm regex: các phần bên trong của trận đấu có thể bắt đầu bằng 0, nhưng toàn bộ số không thể. Hàm regex không thể cho biết sự khác biệt, vì vậy tôi đã loại trừ bất kỳ số nào bắt đầu bằng 0 là trường hợp đặc biệt.

Đầu vào: 1, 4

(?!0|(0)$)(?=([0-4]$))^\d{1,1}$

Phiên bản Regex dài bất hợp lý, 29 ký tự:

print'^',join('|',<>..<>),'$'

Đừng quên rằng có một trường hợp đặc biệt, đó là nếu bằng m0 thì bạn cần cho phép 0 mặc dù nó có số 0 đứng đầu.
Peter Taylor

2
@PeterTaylor, tôi nghĩ "số tự nhiên" có nghĩa là số nguyên dương. Kiểm tra wikipedia, tôi thấy rằng thực sự không có thỏa thuận nào về việc số 0 có phải là số tự nhiên hay không. Tại thời điểm này, tôi chọn cách ẩn náu trong sự mơ hồ thay vì thay đổi giải pháp của mình :)

3

Javascript, điểm 118740839

function makere(m,n){r='(';for(i=m;i<n;)r+=i+++'|';return (r+i)+')';}

Tôi cho rằng bạn có thích điều này hay không phụ thuộc vào cách bạn xác định 'kích thước hợp lý'. :-)


2
Sẽ không kiểm tra điều này. Tôi tin bạn.
tomsmeding

2

Haskell 2063 + 2 * 151 = 2365

Nó được đảm bảo regex được tạo có độ dài O (log n log log n).

matchIntRange 12345 7654321

1(2(3(4(5[6-9]|[6-9]\d)|[5-9]\d\d)|[4-9]\d{3})|[3-9]\d{4})|[2-9]\d{5}|[1-6]\d{6}|7([0-5]\d{5}|6([0-4]\d{4}|5([0-3]\d{3}|4([012]\d\d|3([01]\d|2[01])))))

import Data.Digits

data RegEx = Range Int Int | MatchNone | All Int
            | Or RegEx RegEx | Concat [RegEx] 

alphabet = "\\d"

instance Show RegEx where
  show (Range i j)
   | i == j    = show i
   | i+1 == j  = concat ["[",show i,show j,"]"]
   | i+2 == j  = concat ["[",show i,show (i+1), show (i+2),"]"]
   | otherwise = concat ["[",show i,"-",show j,"]"]
  show (Or a b)  = show a ++ "|" ++ show b
  show MatchNone = "^$"
  show (All n) 
   | n < 3     = concat $ replicate n alphabet
   | otherwise = concat [alphabet,"{",show n,"}"] 
  show e@(Concat xs) 
   | atomic e  = concatMap show xs
   | otherwise = concatMap show' xs
   where show' (Or a b) = "("++show (Or a b)++")"
         show' x = show x
         atomic (Concat xs) = all atomic xs
         atomic (Or _ _)    = False
         atomic _           = True

-- Match integers in a certain range
matchIntRange :: Int->Int->RegEx
matchIntRange a b
 | 0 > min a b = error "Negative input"
 | a > b       = MatchNone
 | otherwise = build (d a) (d b)
 where build :: [Int]->[Int]->RegEx
       build [] [] = Concat [] 
       build (a@(x:xs)) (b@(y:ys))
         | sl && x == y       = Concat [Range x x, build xs ys]
         | sl && all9 && all0 = Concat [Range x y, All n]
         | sl && all0         = Or (Concat [Range x (y-1), All n]) upper
         | sl && all9         = Or lower (Concat [Range (x+1) y, All n])
         | sl && x+1 <= y-1   = Or (Or lower middle) upper
         | sl                 = Or lower upper
         | otherwise          = Or (build a (nines la)) (build (1:zeros la) b)
         where (la,lb) = (length a, length b)
               sl      = la == lb
               n       = length xs
               upper   = Concat [Range y y, build (zeros n) ys]
               lower   = Concat [Range x x, build xs (nines n)]
               middle  = Concat [Range (x+1) (y-1), All n]
               all9    = all (==9) ys
               all0    = all (==0) xs
       zeros n   = replicate n 0
       nines n   = replicate n 9
       d 0 = [0]
       d n = digits 10 n

Mã dưới đây là một phiên bản đơn giản giúp hiểu được thuật toán, nhưng nó không thực hiện bất kỳ tối ưu hóa nào để cải thiện kích thước regex.

matchIntRange 123 4321

(((1((2((3|[4-8])|9)|[3-8]((0|[1-8])|9))|9((0|[1-8])|9))|[2-8]((0((0|[1-8])|9)|[1-8]((0|[1-8])|9))|9((0|[1-8])|9)))|9((0((0|[1-8])|9)|[1-8]((0|[1-8])|9))|9((0|[1-8])|9)))|((1((0((0((0|[1-8])|9)|[1-8]((0|[1-8])|9))|9((0|[1-8])|9))|[1-8]((0((0|[1-8])|9)|[1-8]((0|[1-8])|9))|9((0|[1-8])|9)))|9((0((0|[1-8])|9)|[1-8]((0|[1-8])|9))|9((0|[1-8])|9)))|[2-3]((0((0((0|[1-8])|9)|[1-8]((0|[1-8])|9))|9((0|[1-8])|9))|[1-8]((0((0|[1-8])|9)|[1-8]((0|[1-8])|9))|9((0|[1-8])|9)))|9((0((0|[1-8])|9)|[1-8]((0|[1-8])|9))|9((0|[1-8])|9))))|4((0((0((0|[1-8])|9)|[1-8]((0|[1-8])|9))|9((0|[1-8])|9))|[1-2]((0((0|[1-8])|9)|[1-8]((0|[1-8])|9))|9((0|[1-8])|9)))|3((0((0|[1-8])|9)|1((0|[1-8])|9))|2(0|1)))))

Biểu thức chính quy có 680 ký tự. Đây là mã

import Data.Digits

data RegEx = Range Int Int | MatchNone | Or RegEx RegEx | Concat [RegEx] 

alphabet = "\\d"

instance Show RegEx where
  show (Range i j)
   | i == j    = show i
   | otherwise = concat ["[",show i,"-",show j,"]"]
  show (Or a b)  = concat ["(",show a,"|",show b,")"]
  show MatchNone = "^$"
  show (Concat xs) = concatMap show xs

matchIntRange :: Int->Int->RegEx
matchIntRange a b
 | 0 > min a b = error "Negative input"
 | a > b       = MatchNone
 | otherwise = build (d a) (d b)
 where build :: [Int]->[Int]->RegEx
       build [] [] = Concat [] 
       build (a@(x:xs)) (b@(y:ys))
         | sl && x == y     = Concat [Range x x, build xs ys]
         | sl && x+1 <= y-1 = Or (Or lower middle) upper
         | sl               = Or lower upper
         | otherwise        = Or (build a (nines la)) (build (1:zeros la) b)
         where (la,lb) = (length a, length b)
               sl      = la == lb
               n       = length xs
               upper   = Concat [Range y y, build (zeros n) ys]
               lower   = Concat [Range x x, build xs (nines n)]
               middle  = Concat [Range (x+1) (y-1), build (zeros n) (nines n)]
       zeros n = replicate n 0
       nines n = replicate n 9
       d 0 = [0]
       d n = digits 10 n

2

GolfScript (126 + 2 * 170 = 466)

~)]{`:&,:_,{:i'('\_(<:/i&=48-:D 2<{D^i!!D*|1,*}{'['\i>2D<'-'*D(']?'3$)<}if/D!!*{'\d{'/i>'1,'*_(i-'}|'D}*}%_')'*]}%'(?!'\~'$)'\

Đối với các giá trị đã cho,cung cấp

(?!(\d{1,5}|1([01]\d{4}|2([0-2]\d{3}|3([0-3]\d{2}|4([0-4]\d{1}|5([0-5]))))))$)([1-6]?\d{1,6}|7([0-5]\d{5}|6([0-4]\d{4}|5([0-3]\d{3}|4([0-2]\d{2}|3([01]\d{1}|2([01])))))))

Phân tích để làm theo, nhưng ý tưởng cơ bản là xác định một khối mã ánh xạ một số tự nhiên duy nhất thành một biểu thức khớp với bất kỳ số tự nhiên nhỏ hơn nào, sau đó biến các đầu vào lbubthành một cái nhìn tiêu cực cho (số tự nhiên nhỏ hơn lb) kết hợp với regex cho (số tự nhiên nhỏ hơn ub+1).

Logic khá phức tạp, do đó, ngay cả theo tiêu chuẩn GolfScript, nó cũng khó hiểu. Cho đến khi tôi nhận được một vòng để viết một phân tích chi tiết, đây là danh sách các biến:

&  the whole number string
i  the current idx
D  the current digit
/  not-the-last-digit
_  total number of digits

@ dan1111, tôi đã xem tài liệu về PCRE nhưng tôi không thấy bất cứ điều gì cấm các lớp ký tự không theo thứ tự và trình kiểm tra mà tôi sử dụng không có lỗi. Tôi sẽ phải xem xét điều đó. OTOH nếu công cụ regex của bạn không giống như một biểu thức kết thúc |thì đó là một lỗi trong công cụ regex của bạn, không phải trong regex của tôi.
Peter Taylor

xin lỗi, tôi đã không nhận ra rằng điều đó (a|)thực sự hợp lệ Tuy nhiên, [1-0]trong regex trước đây của bạn không hoạt động trong Perl hoặc trình kiểm tra trực tuyến tôi đã thử.

@ dan1111, sau khi bạn chỉ ra tôi nhận ra rằng trình kiểm tra trực tuyến tôi đang sử dụng đang nuốt lỗi. Tôi đã sao chép nó trên một máy có Perl và viết một khung kiểm tra bằng Perl để kiểm tra các biểu thức chính quy. Cảm ơn đã chỉ ra điều đó.
Peter Taylor
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.