From a7e842f3f74ebb6321c50496d9b145c62095149d Mon Sep 17 00:00:00 2001 From: gkspranger Date: Wed, 27 Jul 2022 16:16:25 -0400 Subject: [PATCH 1/3] good start .. need to figure out curry test exception --- .gitignore | 2 ++ doc/source/api.rst | 1 + toolz/curried/__init__.py | 1 + toolz/functoolz.py | 48 ++++++++++++++++++++++++++++++++--- toolz/tests/test_functoolz.py | 16 ++++++++++-- 5 files changed, 63 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index bf4aebed..e1526799 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ bench/shakespeare.txt *.sw? .DS_STORE \.tox/ +.vscode/ +venv/ diff --git a/doc/source/api.rst b/doc/source/api.rst index 7c1ff662..5d188b03 100644 --- a/doc/source/api.rst +++ b/doc/source/api.rst @@ -65,6 +65,7 @@ Functoolz juxt memoize pipe + thread_as thread_first thread_last diff --git a/toolz/curried/__init__.py b/toolz/curried/__init__.py index 356eddbd..092d9734 100644 --- a/toolz/curried/__init__.py +++ b/toolz/curried/__init__.py @@ -50,6 +50,7 @@ peek, pipe, second, + thread_as, thread_first, thread_last, ) diff --git a/toolz/functoolz.py b/toolz/functoolz.py index 2c75d3a4..a9c489ce 100644 --- a/toolz/functoolz.py +++ b/toolz/functoolz.py @@ -10,9 +10,9 @@ PYPY = hasattr(sys, 'pypy_version_info') and sys.version_info[0] > 2 -__all__ = ('identity', 'apply', 'thread_first', 'thread_last', 'memoize', - 'compose', 'compose_left', 'pipe', 'complement', 'juxt', 'do', - 'curry', 'flip', 'excepts') +__all__ = ('identity', 'apply', 'thread_as', 'thread_first', 'thread_last', + 'memoize', 'compose', 'compose_left', 'pipe', 'complement', 'juxt', + 'do', 'curry', 'flip', 'excepts') PYPY = hasattr(sys, 'pypy_version_info') @@ -65,6 +65,7 @@ def thread_first(val, *forms): g(f(x), y, z) See Also: + thread_as thread_last """ def evalform_front(val, form): @@ -104,6 +105,7 @@ def thread_last(val, *forms): [2, 4] See Also: + thread_as thread_first """ def evalform_back(val, form): @@ -116,6 +118,45 @@ def evalform_back(val, form): return reduce(evalform_back, forms, val) +def thread_as(val, name, *forms): + """ Thread named value through a sequence of functions/forms + + The function, and its inputs, should be specified in a tuple. + The given value name is replaced with the actual value, wherever present. + + >>> def double(x): return 2*x + >>> def inc(x): return x + 1 + >>> v = "somevalname" + >>> thread_as(1, v, (inc, v), (double, v)) + 4 + + >>> def add(x, y): return x + y + >>> def pow(x, y): return x**y + >>> v = "someothervalname" + >>> thread_as(1, v, (add, 4, v), (pow, v, 3)) + 125 + + So in general + thread_as(x, v, (f, v), (g, y, v, z)) + expands to + g(y, f(x), z) + + See Also: + thread_first + thread_last + """ + def arg_val(val, arg): + return val if arg == name else arg + + def evalform_as(val, form): + if isinstance(form, tuple): + func, args = \ + form[0],\ + map(partial(arg_val, val), form[1:]) + return func(*args) + return reduce(evalform_as, forms, val) + + def instanceproperty(fget=None, fset=None, fdel=None, doc=None, classval=None): """ Like @property, but returns ``classval`` when used as a class attribute @@ -621,6 +662,7 @@ def pipe(data, *funcs): See Also: compose compose_left + thread_as thread_first thread_last """ diff --git a/toolz/tests/test_functoolz.py b/toolz/tests/test_functoolz.py index 555cf48d..b0ab2877 100644 --- a/toolz/tests/test_functoolz.py +++ b/toolz/tests/test_functoolz.py @@ -2,8 +2,8 @@ import toolz from toolz.functoolz import (thread_first, thread_last, memoize, curry, compose, compose_left, pipe, complement, do, juxt, - flip, excepts, apply) -from operator import add, mul, itemgetter + flip, excepts, apply, thread_as) +from operator import add, mul, itemgetter, pow from toolz.utils import raises from functools import partial @@ -64,6 +64,18 @@ def test_thread_last(): assert thread_last(2, (add, 5), double) == 14 +def test_thread_as(): + name = "somevalname" + assert thread_as(2, name) == 2 + assert thread_as(2, name, (inc, name)) == 3 + assert thread_as(2, name, (inc, name), (inc, name)) == 4 + assert thread_as(2, name, (double, name), (inc, name)) == 5 + assert thread_as(2, name, (add, name, 5), (double, name)) == 14 + assert list(thread_as([1, 2, 3], name, (map, inc, name), (filter, iseven, name))) == [2, 4] + assert list(thread_as([1, 2, 3], name, (map, inc, name), (filter, isodd, name))) == [3] + assert thread_as(1, name, (add, 4, name), (pow, name, 3)) == 5**3 + + def test_memoize(): fn_calls = [0] # Storage for side effects From b28ab98320ddf7e8915b34b2a84e8c2a61303e83 Mon Sep 17 00:00:00 2001 From: gkspranger Date: Fri, 29 Jul 2022 15:17:11 -0400 Subject: [PATCH 2/3] fixed currying issue --- .gitignore | 4 +--- toolz/curried/__init__.py | 1 + 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index e1526799..aa47766d 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,4 @@ bench/shakespeare.txt .coverage *.sw? .DS_STORE -\.tox/ -.vscode/ -venv/ +\.tox/ \ No newline at end of file diff --git a/toolz/curried/__init__.py b/toolz/curried/__init__.py index 092d9734..83b1b5c3 100644 --- a/toolz/curried/__init__.py +++ b/toolz/curried/__init__.py @@ -99,6 +99,7 @@ update_in = toolz.curry(toolz.update_in) valfilter = toolz.curry(toolz.valfilter) valmap = toolz.curry(toolz.valmap) +thread_as = toolz.curry(toolz.thread_as) del exceptions del toolz From 4412a2d64823d5721b298af9c74b8a60727ef956 Mon Sep 17 00:00:00 2001 From: gkspranger Date: Fri, 29 Jul 2022 15:19:52 -0400 Subject: [PATCH 3/3] ok then --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index aa47766d..bf4aebed 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,4 @@ bench/shakespeare.txt .coverage *.sw? .DS_STORE -\.tox/ \ No newline at end of file +\.tox/