2

私が次のものを持っているとしましょう:

{{こんにちは|こんにちは|ねえ} {世界|地球} | {さようなら|別れ} {noobs|n3wbz|n00blets}}

そして、それを次のいずれかに変えたい:

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

"spinning" 構文の入れ子の仕方に注意してください。私たちが知っている限りでは、10 億層の深さで入れ子になっている可能性があります。

上記の例のようにネストされると、正規表現が台無しになり、結果が正しくない場合を除いて、これは簡単に行うことができます。

誰かが.NET言語またはPythonで例を示してもらえますか?

4

5 に答える 5

5

置換文字列の代わりに関数を受け入れることもできる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'

編集:無限ループを回避し(指摘してくれたBobinceに感謝)、より効率的にするために置き換えsub、空の中括弧も抽出するために置き換えました。これにより、不適切な形式のパターンに対してより堅牢になるはずです。subn{([^{}]+)}{([^{}]*)}

できるだけ多くの情報を 1 行にまとめたい人向け (個人的にはお勧めしません):

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()
于 2009-11-27T23:56:47.977 に答える
4

ブレース セットに別のセットを含めることを禁止し、内側の一致から外側への置換を繰り返し呼び出すだけです。

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'
于 2009-11-28T00:09:37.040 に答える
2

この正規表現インバーターは、pyparsingを使用して一致する文字列を生成します (いくつかの制限があります - + や * のような無制限の繰り返し記号は許可されていません)。{} を () に置き換えて元の文字列を正規表現にすると、インバーターは次のリストを生成します。

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

(スペースが折りたたまれていることは知っていますが、このコードでこの問題に対処する方法についていくつかのアイデアが得られるかもしれません。)

于 2009-11-28T04:15:33.770 に答える
1

インスピレーションを得るには、dada エンジンを参照することをお勧めします。

これに触発されたものの実装をスキームで行い、スキームのASTを活用して私のニーズを表現しました。

具体的には、一般的に正規表現をパーサーとして使用しようとしないことを強くお勧めします。

于 2009-11-27T23:19:26.767 に答える
1

re.finditer を使用して基本的な解析ツリーを構築し、ネスト レベルを決定します。これを行うには、正規表現一致オブジェクトの span 属性を使用します。

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

これにより、括弧で囲まれた式のネストの深さが得られます。パイプに同様のロジックを追加すると、問題の解決に向けて順調に進むでしょう。

于 2009-11-27T23:05:29.370 に答える