optimize Program.curry() (#11162)

* optimize Program.curry()

* Add a comment for this curry implementation

* optimize Program.curry()

* Add comment

Co-authored-by: arty <art.yerkes@gmail.com>
This commit is contained in:
Arvid Norberg
2022-04-20 11:43:15 -07:00
committed by GitHub
co-authored by arty
parent cd9beaca0b
commit 73cd386b87
2 changed files with 50 additions and 4 deletions
+22 -4
View File
@@ -1,12 +1,12 @@
import io
from typing import List, Set, Tuple, Optional
from typing import List, Set, Tuple, Optional, Any
from clvm import SExp
from clvm.casts import int_from_bytes
from clvm.EvalError import EvalError
from clvm.serialize import sexp_from_stream, sexp_to_stream
from chia_rs import MEMPOOL_MODE, run_chia_program, serialized_length, run_generator
from clvm_tools.curry import curry, uncurry
from clvm_tools.curry import uncurry
from chia.types.blockchain_format.sized_bytes import bytes32
from chia.util.hash import std_hash
@@ -88,9 +88,27 @@ class Program(SExp):
cost, r = self.run_with_cost(INFINITE_COST, args)
return r
# Replicates the curry function from clvm_tools, taking advantage of *args
# being a list. We iterate through args in reverse building the code to
# create a clvm list.
#
# Given arguments to a function addressable by the '1' reference in clvm
#
# fixed_args = 1
#
# Each arg is prepended as fixed_args = (c (q . arg) fixed_args)
#
# The resulting argument list is interpreted with apply (2)
#
# (2 (1 . self) rest)
#
# Resulting in a function which places its own arguments after those
# curried in in the form of a proper list.
def curry(self, *args) -> "Program":
cost, r = curry(self, list(args))
return Program.to(r)
fixed_args: Any = 1
for arg in reversed(args):
fixed_args = [4, (1, arg), fixed_args]
return Program.to([2, (1, self), fixed_args])
def uncurry(self) -> Tuple["Program", "Program"]:
r = uncurry(self)
+28
View File
@@ -2,6 +2,9 @@ from unittest import TestCase
from chia.types.blockchain_format.program import Program
from clvm.EvalError import EvalError
from clvm_tools.curry import uncurry
from clvm.operators import KEYWORD_TO_ATOM
from clvm_tools.binutils import assemble, disassemble
class TestProgram(TestCase):
@@ -19,3 +22,28 @@ class TestProgram(TestCase):
self.assertRaises(ValueError, lambda: p.at("q"))
self.assertRaises(EvalError, lambda: p.at("ff"))
def check_idempotency(f, *args):
prg = Program.to(f)
curried = prg.curry(*args)
r = disassemble(curried)
f_0, args_0 = uncurry(curried)
assert disassemble(f_0) == disassemble(f)
assert disassemble(args_0) == disassemble(Program.to(list(args)))
return r
def test_curry_uncurry():
PLUS = KEYWORD_TO_ATOM["+"][0]
f = assemble("(+ 2 5)")
actual_disassembly = check_idempotency(f, 200, 30)
assert actual_disassembly == f"(a (q {PLUS} 2 5) (c (q . 200) (c (q . 30) 1)))"
f = assemble("(+ 2 5)")
args = assemble("(+ (q . 50) (q . 60))")
# passing "args" here wraps the arguments in a list
actual_disassembly = check_idempotency(f, args)
assert actual_disassembly == f"(a (q {PLUS} 2 5) (c (q {PLUS} (q . 50) (q . 60)) 1))"