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 functools或标准库中找不到unfold的定义

展开不是一个python函数…这是一个递归解…

def to_digits(x):
    if not x: return []
    return to_digits(x//10) + [x%10]
to_digits(12345)

对于函数式编程的目的,可以在python中使用map。一个例子是:map(lambda a, b: a+b, [1,2,3], [2,3,4])返回[3, 5, 7] .

在你的情况下,你可以这样做:

def toDigits(num):
    return map(int, str(num))

标准库中没有unfold实现。然而,在more_itertools模块中有一个类似的功能。您可以使用more_itertools。迭代生成数组。可以将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。它可以被认为是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]]

不使用lambda,它使用列表推导式:

def to_digits(x):
    return [int(i) for i in str(x)]

str(x)是一个可迭代对象,具有x的字符串化版本,列表推导式使其成为一个整数列表(因此是int(i))。

相关内容

  • 没有找到相关文章

最新更新