Parantezleri eşleştirerek dizeyi değerlendirin [duplicate]
Bir dizeyi programlı olarak çevirmenin en iyi yolu ne olurdu?
"((abc&(def|ghi))|jkl)&mno"
şu şekilde yürütülecek:
if ((func('abc') and (func('def') or func('ghi'))) or func('jkl')) and func('mno'):
return True
Bunu başarmanın basit bir yolu olmalı gibi hissediyorum, ama bu konuda kafamı kurcalamıyorum.
Yanıtlar
Bu, çözüm için birkaç katman içeren ilginç küçük bir sorundur.
Öncelikle, bu örnek verildiğinde, temel bir infix gösterimi ayrıştırıcısına ihtiyacınız var. Pyparsing'de yerleşik bir yardımcı yöntem vardır infixNotation. Birkaç pyparsing örneği, bir boole ifadesinin infixNotation. İşte örnek ifadenizi ayrıştıracak bir ayrıştırıcı:
import pyparsing as pp
term = pp.Word(pp.alphas)
AND = pp.Literal("&")
OR = pp.Literal("|")
expr = pp.infixNotation(term,
[
(AND, 2, pp.opAssoc.LEFT,),
(OR, 2, pp.opAssoc.LEFT,),
])
print(expr.parseString(sample).asList())
Örneğiniz için bu yazdırılacak:
[[[['abc', '&', ['def', '|', 'ghi']], '|', 'jkl'], '&', 'mno']]
Gördüğünüz gibi sadece ifadeyi değil, parantez içinde gruplamayı da yakaladık.
Ayrıştırma eylemleri ekleyerek istediğiniz çıktıya dönüşümü yapmaya başlayabiliriz. Bunlar, ayrıştırılmış belirteçleri farklı bir değerle değiştirmek için pyparsing'in arayacağı ayrıştırma zamanı geri aramalarıdır (bu bir dize olması gerekmez, değerlendirme için bir AST düğümü olabilir - ancak bu durumda değiştirilmiş bir dize döndüreceğiz).
AND.addParseAction(lambda: " and ")
OR.addParseAction(lambda: " or ")
term.addParseAction(lambda t: "func('{}')".format(t[0]))
expr.addParseAction(lambda t: "({})".format(''.join(t[0])))
Ayrıştırma eylemleri çeşitli imzalara sahip yöntemler olabilir:
function()
function(tokens)
function(location, tokens)
function(input_string, location, tokens)
VE ve VEYA için, ayrıştırılmış operatörleri yalnızca karşılık gelen "ve" ve "veya" anahtar kelimeleriyle değiştirmemiz gerekir. Ayrıştırılmış değişken terimler için, "xxx" yi "func (xxx)" olarak değiştirmek istiyoruz, bu nedenle ayrıştırılmış jetonları alan ve değiştirilmiş dizeyi döndüren bir ayrıştırma eylemi yazıyoruz.
İçin ayrıştırma eylemi exprilginçtir çünkü tek yaptığı ayrıştırılmış içeriği almak, kullanarak bunları birleştirmek ''.join()ve sonra bunu ()s içine sarmaktır. Yana expraslında bir özyinelemeli ifadesidir, bunun çözümlenen iç içe listedeki her düzeyde () 'in doğru sarma yapar s göreceksiniz.
Bu ayrıştırma eylemlerini ekledikten sonra parseString(), şimdi şunu vererek tekrar aramayı deneyebiliriz :
["(((func('abc') and (func('def') or func('ghi'))) or func('jkl')) and func('mno'))"]
Yakınlaşmak!
Biçimlendirmeyi istediğiniz ififadeye dönüştürmek için başka bir ayrıştırma eylemi kullanabiliriz. Ancak expr, expr(ve ilişkili ayrıştırma eyleminin) tüm iç içe yerleştirme düzeylerinde ayrıştırılacağını gördüğümüz için , bu ayrıştırma eylemine doğrudan iliştiremeyiz . Bunun yerine, ifadenin "dış" bir versiyonunu oluşturabiliriz, bu sadece bir ifadenin kapsayıcı ifadesidir:
outer_expr = pp.Group(expr)
Ayrıştırma eylemi, gördüğümüze benzer expr, burada girdi belirteçlerini kullanarak yeni bir dize döndürürüz:
def format_expression(tokens):
return "if {}:\n return True".format(''.join(tokens[0]))
outer_expr.addParseAction(format_expression)
Şimdi outer_exprgirdi dizesini ayrıştırmak için kullanıyoruz :
print(outer_expr.parseString(sample)[0])
Başlarken:
if (((func('abc') and (func('def') or func('ghi'))) or func('jkl')) and func('mno')):
return True
(Bu değerde fazladan bir () kümesi olabilir, outer_expristenirse ayrıştırma işleminde kaldırılabilirler .)
Ayrıştırıcının bitmiş sürümü (ayrıştırıcı işlevinin ilerlemesini görmek için ara yazdırma deyimlerinin açıklamasını kaldırın):
sample = "((abc&(def|ghi))|jkl)&mno"
import pyparsing as pp
term = pp.Word(pp.alphas)
AND = pp.Literal("&")
OR = pp.Literal("|")
expr = pp.infixNotation(term,
[
(AND, 2, pp.opAssoc.LEFT,),
(OR, 2, pp.opAssoc.LEFT,),
])
# print(expr.parseString(sample).asList())
AND.addParseAction(lambda: " and ")
OR.addParseAction(lambda: " or ")
term.addParseAction(lambda t: "func('{}')".format(t[0]))
expr.addParseAction(lambda t: "({})".format(''.join(t[0])))
# print(expr.parseString(sample).asList())
def format_expression(tokens):
return "if {}:\n return True".format(''.join(tokens[0]))
outer_expr = pp.Group(expr).addParseAction(format_expression)
print(outer_expr.parseString(sample)[0])
Peki, dizeniz gösterdiğinizden daha karmaşık değilse (örneğin sadece bu semboller artı harflerden / sayılardan oluşuyorsa), gerektiğinde eşleşmeleri değiştiren bazı basit normal ifadelerle ayrıştırabilirsiniz. Bundan sonra, eval()onu python kodu olarak çalıştırmak için kullanabilirsiniz .
Örneğin:
import re
def func(x):
# just an example...
return True
s = "((abc&(def|ghi))|jkl)&mno"
s = re.sub(r'(\w+)', r"func('\1')", s)
s = s.replace('&', ' and ')
s = s.replace('|', ' or ')
print(s)
print(eval(s))
Çıktı:
((func('abc') and (func('def') or func('ghi'))) or func('jkl')) and func('mno')
True