سؤال

لنفترض أن لدي ما يلي:

{{Hello | Hi | Hey} {World | Earth} | {وداعا | وداع} {noobs | n3wbz | n00blets}}

وأريد أن يتحول ذلك إلى أي مما يلي:

Hello world 
Goodbye noobs 
Hi earth
farewell n3wbz 
// etc.

الانتباه إلى طريقة تداخل بناء الجملة "الدوران".يمكن أن تكون متداخلة على عمق مليار طبقة لكل ما نعرفه.

يمكنني القيام بذلك بسهولة، إلا أنه بمجرد تداخلها مثل المثال أعلاه، فإن التعبير العادي الخاص بي يفسد والنتائج غير صحيحة.

هل يمكن لشخص ما أن يعرض مثالاً إما بلغة .NET أو Python من فضلك؟

هل كانت مفيدة؟

المحلول

طريقة بسيطة مع re.subn, ، والتي يمكنها أيضًا قبول دالة بدلاً من سلسلة بديلة:

import re
from random import randint

def select(m):
    choices = m.group(1).split('|')
    return choices[randint(0, len(choices)-1)]

def spinner(s):
    r = re.compile('{([^{}]*)}')
    while True:
        s, n = r.subn(select, s)
        if n == 0: break
    return s.strip()

إنه ببساطة يستبدل جميع الخيارات العميقة التي يقابلها، ثم يتكرر حتى لا يبقى أي خيار. subn تُرجع صفًا بالنتيجة وعدد الاستبدالات التي تم إجراؤها، وهو أمر مناسب لاكتشاف نهاية المعالجة.

نسختي من select() يمكن استبداله بـ Bobince الذي يستخدم random.choice() ويكون أكثر أناقة إذا كنت تريد فقط الالتزام بمحدد عشوائي.إذا كنت ترغب في إنشاء شجرة اختيار، فيمكنك توسيع الوظيفة المذكورة أعلاه، ولكنك ستحتاج إلى متغيرات عامة لتتبع مكانك، لذا فإن نقل الوظائف إلى فئة سيكون منطقيًا.هذا مجرد تلميح، ولن أطور هذه الفكرة لأنه لم يكن السؤال الأصلي حقًا.

لاحظ أخيرًا أنه يجب عليك استخدام r.subn(select, s, re.U) إذا كنت بحاجة إلى سلاسل Unicode (s = u"{...}")

مثال:

>>> s = "{{Hello|Hi|Hey} {world|earth} | {Goodbye|farewell} {noobs|n3wbz|n00blets}}"
>>> print spinner(s)
'farewell n3wbz'

يحرر: تم استبداله sub بواسطة subn لتجنب الحلقة اللانهائية (بفضل Bobince للإشارة إليها) وجعلها أكثر كفاءة واستبدالها {([^{}]+)} بواسطة {([^{}]*)} لاستخراج الأقواس المتعرجة الفارغة أيضًا.وهذا من شأنه أن يجعله أكثر قوة للأنماط سيئة التنسيق.

بالنسبة للأشخاص الذين يحبون وضع أكبر قدر ممكن في سطر واحد (وهو ما لا أشجعه شخصيًا):

def spin(s):
    while True:
        s, n = re.subn('{([^{}]*)}',
                       lambda m: random.choice(m.group(1).split("|")),
                       s)
        if n == 0: break
    return s.strip()

نصائح أخرى

ويجب أن تكون بسيطة إلى حد ما، مجرد السماح هدفين تعيينها من بينها آخر، ثم استدعاء مرارا القيام البدلاء من المباريات الداخلية إلى الخارج:

def replacebrace(match):
    return random.choice(match.group(1).split('|'))

def randomizebraces(s):
   while True:
       s1= re.sub(r'\{([^{}]*)\}', replacebrace, s)
       if s1==s:
           return s
       s= s1

>>> randomizebraces('{{Hello|Hi|Hey} {world|earth}|{Goodbye|farewell} {noobs|n3wbz|n00blets}}')
'Hey world'
>>> randomizebraces('{{Hello|Hi|Hey} {world|earth}|{Goodbye|farewell} {noobs|n3wbz|n00blets}}')
'Goodbye noobs'

العاكس التعابير المنطقية الاستخدامات <لأ href = "HTTP: // pyparsing.wikispaces.com/ "يختلط =" نوفولو noreferrer "> pyparsing لتوليد مطابقة السلاسل (مع بعض القيود - حرف التكرار غير محدود مثل + و * لا يسمح). إذا قمت باستبدال {} الصورة مع () الصورة لجعل السلسلة الأصلية الخاصة بك إلى التعابير المنطقية، يولد العاكس لهذه القائمة:

Helloworld
Helloearth
Hiworld
Hiearth
Heyworld
Heyearth
Goodbyenoobs
Goodbyen3wbz
Goodbyen00blets
farewellnoobs
farewelln3wbz
farewelln00blets

(وأنا أعلم وانهار المساحات، ولكن ربما هذا الرمز سوف اعطيكم بعض الأفكار حول كيفية مهاجمة هذه المشكلة.)

وأود أن استخدام re.finditer وبناء شجرة تحليل الأساسية لتحديد مستوى التداخل. للقيام بذلك، وأود أن استخدام السمة مدى الكائن يتطابق مع التعبير العادي:

text = '{{Hello|Hi|Hey} {world|earth} | {Goodbye|farewell} {noobs|n3wbz|n00blets}}'

import re
re_bracks = re.compile(r'{.+?}')

# subclass list for a basic tree datatype
class bracks(list):
    def __init__(self, m):
        self.m = m

# icky procedure to create the parse tree
# I hate these but don't know how else to do it
parse_tree = []
for m in re_bracks.finditer(text):
    if not this_element:
        # this first match
        parse_tree.extend(element(m))
    else:
        # ... and all the rest
        this_element = bracks(m)
        this_start, this_end = m.span()

        # if this match is nested in the old one ...
        if this_start < previous_start and this_end > previous_end:
            # nest it inside the previous one
            previous_element.extend(this_element) 
        else:
            # otherwise make it a child of the parse_tree
            parse_tree.extend(element(m))

        previous_element = this_element
        previous_start, previous_end = this_start, this_end

وهذا من شأنه أن تعطيك عمق التعشيش من التعبيرات بين قوسين. إضافة بعض منطق مماثل لأنابيب وكنت يكون جيدا على طريقك إلى حل المشكلة.

وأنا أوصي أن نلقي نظرة على محرك دادا للحصول على الإلهام.

ولقد فعلت لتنفيذ شيء مستوحاة من هذا في مخطط وAST مخطط الاستدانة في التعبير عن احتياجاتي.

وعلى وجه التحديد، أنصح بقوة ضد محاولة استخدام التعابير المنطقية كما محلل بشكل عام.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top