2012-10-16 37 views
5

PostgreSQL cho phép tạo chỉ mục trên biểu thức, ví dụ: CREATE INDEX ON films ((lower(title))). Nó cũng có chức năng thông tin pg_get_expr() chuyển định dạng nội dung của biểu thức thành văn bản, tức là, lower(title) trong ví dụ trước. Các biểu thức có thể nhận được khá lông ở lần. Dưới đây là một số ví dụ (bằng Python):Tách danh sách các biểu thức hàm lồng nhau trong Python

sample_exprs = [ 
    'lower(c2)', 
    'lower(c2), lower(c3)', 
    "btrim(c3, 'x'::text), lower(c2)", 
    "date_part('month'::text, dt), date_part('day'::text, dt)", 
    '"substring"(c2, "position"(c2, \'_begin\'::text)), "substring"(c2, "position"(c2, \'_end\'::text))', 
    "(((c2)::text || ', '::text) || c3), ((c3 || ' '::text) || (c2)::text)", 
    'f1(f2(arga, f3()), arg1), f4(arg2, f5(argb, argc)), f6(arg3)'] 

Mục cuối cùng không thực sự từ Postgres nhưng chỉ là một ví dụ cực kỳ về mã của tôi nên xử lý.

Tôi đã viết một hàm Python để phân tách các danh sách văn bản thành các biểu thức thành phần. Ví dụ, chi tiết cuối cùng được chia thành:

f1(f2(arga, f3()), arg1) 
f4(arg2, f5(argb, argc)) 
f6(arg3) 

tôi đã thử nghiệm với str phương pháp như find()count() và cũng coi regexes, nhưng cuối cùng tôi đã viết một chức năng đó là những gì tôi đã viết bằng C (về cơ bản đếm mở và đóng parens để tìm nơi để phá vỡ các văn bản). Đây là hàm:

def split_exprs(idx_exprs): 
    keyexprs = [] 
    nopen = nclose = beg = curr = 0 
    for c in idx_exprs: 
     curr += 1 
     if c == '(': 
      nopen += 1 
     elif c == ')': 
      nclose += 1 
      if nopen > 0 and nopen == nclose: 
       if idx_exprs[beg] == ',': 
        beg += 1 
       if idx_exprs[beg] == ' ': 
        beg += 1 
       keyexprs.append(idx_exprs[beg:curr]) 
       beg = curr 
       nopen = nclose = 0 
    return keyexprs 

Câu hỏi đặt ra là liệu có cách nào hay hơn để sử dụng regexes để giải quyết vấn đề này.

+1

Có một cái nhìn tại [ pyparsing] (http://pyparsing.wikispaces.com/) cho một số cảm hứng –

+0

Regexes có thể không có bất cứ điều gì thanh lịch. Xem: http://perldoc.perl.org/perlfaq6.html#Can-I-use-Perl-regular-expressions-to-match-balanced-text%3f – Himanshu

+0

Có, tôi đã tin rằng các regex không thể được sử dụng vì các máy trạng thái không thể đếm số lồng của các dấu ngoặc đơn mà không có sự trợ giúp của một chồng, ví dụ, một PDA là cần thiết. –

Trả lời

1

Nếu bạn đang tìm cách làm cho nó trở nên trầm cảm hơn, cách duy nhất tôi có thể nghĩ là dễ đọc.

Ngoài ra, tôi tránh một nhánh và một biến bằng cách đếm ngăn xếp. Đề xuất pythonic duy nhất tôi có thể đưa ra là sử dụng biến 'index' với hàm enumerate(...). Như trong

for i, j in enumerate(<iterable>)

này sẽ tạo ra một biến, i, mà sẽ bằng với số vòng lặp hiện tại, nơi j sẽ biến lặp dự kiến.

def split_fns(fns): 
    paren_stack_level = 0 
    last_pos = 0 
    output = [] 
    for curr_pos, curr_char in enumerate(fns): 
     if curr_char == "(": 
      paren_stack_level += 1 
     elif curr_char == ")": 
      paren_stack_level -= 1 
      if not paren_stack_level: 
       output.append(fns[last_pos:curr_pos+1].lstrip(" ,")) 
       last_pos = curr_pos+1 
    return output 

for i in sample_exprs: 
    print(split_fns(i)) 
+0

Biến stack_level duy nhất chắc chắn là một sự cải tiến. Tuy nhiên, phạm vi (len (fns)) không tấn công tôi như pythonic, và tôi không biết nếu truy cập mỗi nhân vật như fns [curr_pos] là hiệu quả hơn (bản năng của tôi nói không, nhưng tôi đã không đánh giá nó) . –

+0

@JoeAbbate 'phạm vi (len ())' không phải là vô cùng phổ biến trong python, theo kinh nghiệm của tôi. Và bạn nói đúng, 'fns [curr_pos]' không hiệu quả. Tôi sẽ chỉnh sửa câu trả lời – jsvk

1

Đây là phiên bản của tôi, hơn pythonic, ít lộn xộn Tôi nghĩ, và hoạt động trên dòng ký tự, mặc dù tôi không nhìn thấy bất kỳ lợi thế ở chỗ :)

def split_fns(fns): 
    level = 0 
    stack = [[]] 
    for ch in fns: 
     if level == 0 and ch in [' ',',']: 
      continue   
     stack[-1].append(ch) 

     if ch == "(": 
      level += 1 
     elif ch == ")": 
      level -= 1 
      if level == 0: 
       stack.append([]) 

    return ["".join(t) for t in stack if t] 
+0

Tôi ấn tượng với tư duy bên, mặc dù có một điều cần cân nhắc: Đối với các ký tự bên ngoài ngăn xếp, các so sánh 2-5 được thực hiện, 2-3 nếu chúng là biểu tượng. 2-4 cho dấu ngoặc đơn và 4 để xác định hàm hoàn tất. Ví dụ, số của tôi là 2, 2, 1-2 và 5-7 (có khả năng 4 nếu viết lại thông minh) – jsvk

+0

@jsvk Tôi cho rằng tốc độ rõ ràng hơn là tiêu chí ở đây, nếu không tôi sẽ viết nó trong C –

+0

có nhiều lý do để viết mã hiệu quả hơn tốc độ, nhưng tôi thấy bạn đến từ đâu; sự rõ ràng là một phần quan trọng của việc bị kích thích. – jsvk

Các vấn đề liên quan