zlib などで使われている CRC32 は以下のような計算になっています。
Python3
def crc32(data, P=0xedb88320, v=0):
v = v ^ 0xffffffff
for d in data:
v = v ^ d
for b in range(8):
v = (v >> 1) ^ ((v & 1) and P)
return v ^ 0xffffffff
これを、一つの lambda 式にしようとしたとき、data に応じた変数 v の更新が必要ですが、 Python 3.7 以前では v を更新する方法を見つけられませんでした。しかし、Python 3.8 から代入式 ":="
が使えるので、それが可能です。
Python3.8
crc32 = lambda data, P=0xedb88320, v=0xffffffff: 0xffffffff ^ [v := ((v ^ (0 if b else d)) >> 1) ^ (((v ^ (0 if b else d)) & 1) and P) for d in data for b in range(8)][-1]
この式は、入力ビット数(入力バイト数の 8 倍)の配列を一度作るので、CRC32 としての実用性はゼロです。
zlib の crc32 と一致するか確認します。
test.py
#!/usr/bin/env python3.8
import zlib
def crc32a(data, P=0xedb88320, v=0):
v ^= 0xffffffff
for d in data:
v ^= d
for b in range(8):
v = (v >> 1) ^ ((v & 1) and P)
return v ^ 0xffffffff
crc32b = lambda data, P=0xedb88320, v=0xffffffff: 0xffffffff ^ [v := ((v ^ (0 if b else d)) >> 1) ^ (((v ^ (0 if b else d)) & 1) and P) for d in data for b in range(8)][-1]
def test(data, encoding='utf-8'):
data = bytearray(data) if type(data) != str else bytearray(data, encoding)
a = crc32a(data)
b = crc32b(data)
z = zlib.crc32(data)
if a != z:
raise Exception('BUG(crc32a): %#010x != %#010x' % (a, z))
if b != z:
raise Exception('BUG(crc32b): %#010x != %#010x' % (b, z))
print('OK: %#010x' % z)
test('abcd')
test(b'efgh')
実行結果
$ python3.8 test.py
OK: 0xed82cd11
OK: 0x08337bb5