演習として、そして主に私自身の娯楽のために、私はバックトラッキングpackratパーサーを実装しています。このためのインスピレーションは、アルゴルのような言語でハイジェニックマクロがどのように機能するかについて、もっとよく理解したいと思います(通常、構文構文のないlisp方言に見られるように)。このため、入力を通過するパスによって文法が異なる可能性があるため、キャッシュされた解析結果とともに現在のバージョンの文法も保存しない限り、キャッシュされた解析結果は無効です。(編集:キーと値のコレクションのこの使用の結果は、それらが不変でなければならないということですが、それらを変更できるようにするためにインターフェースを公開するつもりはないので、可変または不変のコレクションは問題ありません)
問題は、Python dictが他のdictのキーとして表示できないことです。タプルを使っても(とにかくやっているように)助けにはなりません。
>>> cache = {}
>>> rule = {"foo":"bar"}
>>> cache[(rule, "baz")] = "quux"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'dict'
>>>
ずっと下のタプルである必要があると思います。現在、Python標準ライブラリは、ほぼ必要なものを提供しcollections.namedtuple
、非常に異なる構文を持っていますが、キーとして使用できます。上記のセッションから続く:
>>> from collections import namedtuple
>>> Rule = namedtuple("Rule",rule.keys())
>>> cache[(Rule(**rule), "baz")] = "quux"
>>> cache
{(Rule(foo='bar'), 'baz'): 'quux'}
OK。しかし、私は使用したいルールのキーの可能な組み合わせごとにクラスを作成する必要があります。これはそれほど悪くはありません。各解析ルールが使用するパラメーターを正確に認識しているため、クラスを同時に定義できますルールを解析する関数として。
編集:namedtuple
s のもう1つの問題は、それらが厳密に定位置にあることです。異なるように見える2つのタプルは、実際には同じである可能性があります。
>>> you = namedtuple("foo",["bar","baz"])
>>> me = namedtuple("foo",["bar","quux"])
>>> you(bar=1,baz=2) == me(bar=1,quux=2)
True
>>> bob = namedtuple("foo",["baz","bar"])
>>> you(bar=1,baz=2) == bob(bar=1,baz=2)
False
tl'dr:dict
他dict
ののキーとして使用できるを取得するにはどうすればよいですか?
答えに少し手を加えたので、これが私が使用しているより完全なソリューションです。これは、結果として得られるディクテーションを実際的な目的のために漠然と不変にするために少し余分な作業を行うことに注意してください。もちろん、電話でハッキングするのは非常に簡単dict.__setitem__(instance, key, value)
ですが、私たちはここではすべて大人です。
class hashdict(dict):
"""
hashable dict implementation, suitable for use as a key into
other dicts.
>>> h1 = hashdict({"apples": 1, "bananas":2})
>>> h2 = hashdict({"bananas": 3, "mangoes": 5})
>>> h1+h2
hashdict(apples=1, bananas=3, mangoes=5)
>>> d1 = {}
>>> d1[h1] = "salad"
>>> d1[h1]
'salad'
>>> d1[h2]
Traceback (most recent call last):
...
KeyError: hashdict(bananas=3, mangoes=5)
based on answers from
http://stackoverflow.com/questions/1151658/python-hashable-dicts
"""
def __key(self):
return tuple(sorted(self.items()))
def __repr__(self):
return "{0}({1})".format(self.__class__.__name__,
", ".join("{0}={1}".format(
str(i[0]),repr(i[1])) for i in self.__key()))
def __hash__(self):
return hash(self.__key())
def __setitem__(self, key, value):
raise TypeError("{0} does not support item assignment"
.format(self.__class__.__name__))
def __delitem__(self, key):
raise TypeError("{0} does not support item assignment"
.format(self.__class__.__name__))
def clear(self):
raise TypeError("{0} does not support item assignment"
.format(self.__class__.__name__))
def pop(self, *args, **kwargs):
raise TypeError("{0} does not support item assignment"
.format(self.__class__.__name__))
def popitem(self, *args, **kwargs):
raise TypeError("{0} does not support item assignment"
.format(self.__class__.__name__))
def setdefault(self, *args, **kwargs):
raise TypeError("{0} does not support item assignment"
.format(self.__class__.__name__))
def update(self, *args, **kwargs):
raise TypeError("{0} does not support item assignment"
.format(self.__class__.__name__))
# update is not ok because it mutates the object
# __add__ is ok because it creates a new object
# while the new object is under construction, it's ok to mutate it
def __add__(self, right):
result = hashdict(self)
dict.update(result, right)
return result
if __name__ == "__main__":
import doctest
doctest.testmod()
hashdict
あなたはそれをハッシュ開始した後、なぜキャッシュしませ少なくとも、不変でなければならないkey
とhash
の属性として値をhashdict
オブジェクト?を変更__key()
し__hash__()
てテストし、はるかに高速であることを確認しました。SOではコメント内のフォーマットされたコードは許可されていないため、ここにリンクします。sam.aiki.info