python 中的展开函数
unfold function in python
我最近在python开始练习函数式编程。
假设我定义了一个获取数字数组并将其连接起来的函数:
In [1]: def fromDigits(digits):
...: return reduce(lambda x,y: 10*x+y,digits,0)
...:
In [2]: fromDigits([1,2,3,4,5])
Out[2]: 12345
现在我要实现反向功能:
def toDigits(num):
return unfold(lambda m,digits: (m/10,digits+[m % 10]) if m>0 else None, num, [])
但我无法在 python
函数工具或标准库中的任何地方找到 unfold
的定义。
unfold 不是 python 函数...这是一个递归解决方案...
def to_digits(x):
if not x: return []
return to_digits(x//10) + [x%10]
to_digits(12345)
这不使用 lambda
,它使用列表理解:
def to_digits(x):
return [int(i) for i in str(x)]
str(x)
是一个带有 x
字符串化版本的可迭代对象,列表推导式使其成为一个整数列表(因此 int(i)
)。
出于函数式编程的目的,可以在python中使用map
。
一个例子是:
map(lambda a, b: a+b, [1,2,3], [2,3,4])
这将 return [3, 5, 7]
.
对于你的情况,你可以这样做:
def toDigits(num):
return map(int, str(num))
标准库中没有unfold
实现。不过,在more_itertools
模块中也有类似的功能。您可以使用 more_itertools.iterate 生成数组。 more_itertools.iterate
可以被认为是 unfold
的非终止版本,因此您需要 itertools.takewhile
来终止迭代器。
from more_itertools import iterate, always_reversible
from itertools import takewhile
def to_digits(num):
return always_reversible(
map(
lambda x: x % 10,
takewhile(
lambda x: x > 0,
iterate(lambda x: x // 10, num)
)
)
)
print(*to_digits(12345))
如果你更喜欢标准库中的函数,你可能想使用 itertools.accumulate,它可以被认为是 more_itertools.iterate
的更强大版本,因为 itertools.accumulate
需要一个额外的可迭代参数。由于您不需要额外的可迭代来生成数字,只需传递 repeat(None)
即可退回到 more_itertools.iterate
行为。
from itertools import takewhile, accumulate, repeat
def to_digits(num):
return reversed(
tuple(
map(
lambda x: x % 10,
takewhile(
lambda x: x > 0,
accumulate(repeat(None), lambda x, _: x // 10, initial=num)
)
)
)
)
print(*to_digits(12345))
unfoldr
的解决方案
# Python 3.8+
from collections import deque
def unfold(s, f):
state, elements = s, deque()
while (next_step := f(state)) is not None:
state, element = next_step
elements.appendleft(element)
return list(elements)
def to_digits(num):
return unfold(num, lambda n: (n // 10, n % 10) if n > 0 else None)
>>> to_digits(12345)
[1, 2, 3, 4, 5]
来自问题
问题中提出的逻辑的不同部分是:
def toDigits(num):
return unfold(lambda m,digits: (m/10,digits+[m % 10]) if m>0 else None, num, [])
# +--+ +----+ +-+
# compute the next state <--------+ | |
# | |
# compute the element <------------------------+ |
# |
# predicate to terminate `unfold` <------------------------+
但是我认为以下这些部分不应该属于要传递的函数,而应该属于 unfold
逻辑本身:
def toDigits(num):
return unfold(lambda m,digits: (m/10,digits+[m % 10]) if m^0 else None, num, [])
# +----+ +------+ ++ ++
新函数
传递给 unfold
的函数可以简单地是:
lambda n: (n // 10, n % 10) if n > 0 else None
#+------+ ++
# | +---> floor division operator
# |
# +---------> input just the number
# output the next state and the element "unfolded"
# (or None)
类型签名为:
S = TypeVar('S') # state
A = TypeVar('A') # element to unfold
f: Callable[[S], Optional[Tuple[S, A]]] = lambda n: (n // 10, n % 10) if n > 0 else None
# +-+ +--+
# | |
# input <-+ |
# |
# output <---------------------+
展开(r)
看看问题中提出的逻辑,我们可以假设 unfold
预期实际上是“右手风味”unfoldr
。一个可能的实现(Python 3.8+)可能是:
from collections import deque # O(1) `appendleft`
def unfoldr(s, f):
state, elements = s, deque()
while (next_step := f(state)) is not None:
state, element = next_step
elements.appendleft(element)
return list(elements)
签名所在位置:
def unfoldr(state: S, f: Callable[[S], Optional[Tuple[S, A]]]) -> List[A]:
顺便说一句,如果您更喜欢使用递归实现创建堆栈帧:
def unfoldr(state, f):
return [] if (next_step := f(state)) is None else unfoldr(next_step[0], f) + [next_step[1]]
我最近在python开始练习函数式编程。
假设我定义了一个获取数字数组并将其连接起来的函数:
In [1]: def fromDigits(digits):
...: return reduce(lambda x,y: 10*x+y,digits,0)
...:
In [2]: fromDigits([1,2,3,4,5])
Out[2]: 12345
现在我要实现反向功能:
def toDigits(num):
return unfold(lambda m,digits: (m/10,digits+[m % 10]) if m>0 else None, num, [])
但我无法在 python
函数工具或标准库中的任何地方找到 unfold
的定义。
unfold 不是 python 函数...这是一个递归解决方案...
def to_digits(x):
if not x: return []
return to_digits(x//10) + [x%10]
to_digits(12345)
这不使用 lambda
,它使用列表理解:
def to_digits(x):
return [int(i) for i in str(x)]
str(x)
是一个带有 x
字符串化版本的可迭代对象,列表推导式使其成为一个整数列表(因此 int(i)
)。
出于函数式编程的目的,可以在python中使用map
。
一个例子是:
map(lambda a, b: a+b, [1,2,3], [2,3,4])
这将 return [3, 5, 7]
.
对于你的情况,你可以这样做:
def toDigits(num):
return map(int, str(num))
标准库中没有unfold
实现。不过,在more_itertools
模块中也有类似的功能。您可以使用 more_itertools.iterate 生成数组。 more_itertools.iterate
可以被认为是 unfold
的非终止版本,因此您需要 itertools.takewhile
来终止迭代器。
from more_itertools import iterate, always_reversible
from itertools import takewhile
def to_digits(num):
return always_reversible(
map(
lambda x: x % 10,
takewhile(
lambda x: x > 0,
iterate(lambda x: x // 10, num)
)
)
)
print(*to_digits(12345))
如果你更喜欢标准库中的函数,你可能想使用 itertools.accumulate,它可以被认为是 more_itertools.iterate
的更强大版本,因为 itertools.accumulate
需要一个额外的可迭代参数。由于您不需要额外的可迭代来生成数字,只需传递 repeat(None)
即可退回到 more_itertools.iterate
行为。
from itertools import takewhile, accumulate, repeat
def to_digits(num):
return reversed(
tuple(
map(
lambda x: x % 10,
takewhile(
lambda x: x > 0,
accumulate(repeat(None), lambda x, _: x // 10, initial=num)
)
)
)
)
print(*to_digits(12345))
unfoldr
的解决方案
# Python 3.8+
from collections import deque
def unfold(s, f):
state, elements = s, deque()
while (next_step := f(state)) is not None:
state, element = next_step
elements.appendleft(element)
return list(elements)
def to_digits(num):
return unfold(num, lambda n: (n // 10, n % 10) if n > 0 else None)
>>> to_digits(12345)
[1, 2, 3, 4, 5]
来自问题
问题中提出的逻辑的不同部分是:
def toDigits(num):
return unfold(lambda m,digits: (m/10,digits+[m % 10]) if m>0 else None, num, [])
# +--+ +----+ +-+
# compute the next state <--------+ | |
# | |
# compute the element <------------------------+ |
# |
# predicate to terminate `unfold` <------------------------+
但是我认为以下这些部分不应该属于要传递的函数,而应该属于 unfold
逻辑本身:
def toDigits(num):
return unfold(lambda m,digits: (m/10,digits+[m % 10]) if m^0 else None, num, [])
# +----+ +------+ ++ ++
新函数
传递给 unfold
的函数可以简单地是:
lambda n: (n // 10, n % 10) if n > 0 else None
#+------+ ++
# | +---> floor division operator
# |
# +---------> input just the number
# output the next state and the element "unfolded"
# (or None)
类型签名为:
S = TypeVar('S') # state
A = TypeVar('A') # element to unfold
f: Callable[[S], Optional[Tuple[S, A]]] = lambda n: (n // 10, n % 10) if n > 0 else None
# +-+ +--+
# | |
# input <-+ |
# |
# output <---------------------+
展开(r)
看看问题中提出的逻辑,我们可以假设 unfold
预期实际上是“右手风味”unfoldr
。一个可能的实现(Python 3.8+)可能是:
from collections import deque # O(1) `appendleft`
def unfoldr(s, f):
state, elements = s, deque()
while (next_step := f(state)) is not None:
state, element = next_step
elements.appendleft(element)
return list(elements)
签名所在位置:
def unfoldr(state: S, f: Callable[[S], Optional[Tuple[S, A]]]) -> List[A]:
顺便说一句,如果您更喜欢使用递归实现创建堆栈帧:
def unfoldr(state, f):
return [] if (next_step := f(state)) is None else unfoldr(next_step[0], f) + [next_step[1]]