<p>我的第一个建议是不要将<code>arrayOfTrigrams</code>完全保存在内存中,而是使用流式处理。你是从某个地方读的,所以你可以控制你读它的方式。Python的生成器在这里非常方便。假设你是从文件中读的:</p>
<pre><code>def read_trigrams(fobj):
unique = {}
def make_unique(w):
w = w.strip("\"'`!?,.():-;{}").lower()
return unique.setdefault(w, w)
fobj.seek(0, 2)
total_size = fobj.tell()
fobj.seek(0, 0)
read = 0
prev_words = []
for idx, line in enumerate(fobj):
read += len(line)
words = prev_words
words.extend(filter(None, (make_unique(w) for w in line.split())))
if len(words) > 3:
for i in range(len(words) - 3):
yield tuple(words[i:i+3])
prev_words = words[-2:]
</code></pre>
<p>这里有两件事:</p>
<ol>
<li>我们使用的是一个生成器,所以我们不需要读取整个文件并返回一个三元组列表,而是逐个返回三元组。这有点慢,但可以节省内存。在</li>
<li>我们最终通过对字符串的dict来确保我们读取的每个字符串最多有一个副本。虽然一开始看起来很奇怪,但是从文件<code>N</code>时间读取相同的字节序列<code>S</code>确实占用了<code>N*len(S)</code>个字节。通过使用字典,我们确保输入中每个单词都有一个唯一的副本。当然,这会消耗一些内存。在</li>
</ol>
<p>这个函数对你来说可能不同,这取决于你从哪里读到你的八卦。请记住,我在这里使用的标记器是非常基本的。在</p>
<p>这已经节省了一点内存,但不是太多。在</p>
<p>所以,让我们将中间结果存储在磁盘上:</p>
^{pr2}$
<p>在这一步中,您可以调整<code>LIMIT</code>以不使用太多内存,也就是说,只要减少它,直到您不再使用<code>MemoryError</code>。在</p>
<p>现在,驱动器上有<code>N</code>个文件,其中有一个经过排序的三元组列表。在一个单独的程序中,您可以读入并汇总所有中间计数:</p>
<pre><code>import sys
import pickle
def merger(inputs):
unpicklers = [pickle.Unpickler(open(f, 'rb')) for f in inputs]
DONE = (object(), )
NEXT = (object(), )
peek = [NEXT] * len(unpicklers)
while True:
for idx in range(len(unpicklers)):
if peek[idx] is NEXT:
try:
peek[idx] = unpicklers[idx].load()
except EOFError:
peek[idx] = DONE
if all(v is DONE for v in peek):
return
min_key = min(v[0] for v in peek if v is not DONE)
yield min_key, sum(v[1] for v in peek if v[0] == min_key)
peek = [NEXT if (v[0] == min_key) else v for v in peek]
for trigram, count in merger(sys.argv[1:]):
print(trigram, count)
</code></pre>
<p>如果你有4吉布的内存,你实际上可能不得不使用拆分功能。有了8吉布斯,你应该能够保持所有的内存。在</p>