【问题标题】:Using reduce() to get frequency of every character pair/triple in a string?使用 reduce() 获取字符串中每个字符对/三元组的频率?
【发布时间】:2020-08-23 07:00:01
【问题描述】:

我应该以一种功能性的方式完成这项任务,所以没有任何形式的循环。

我已经完成了寻找单个字符频率的任务。我的代码如下所示:

char_freq = {}

    def getcharfreq(char_freq, ch):
        char_freq[ch] = char_freq.get(ch, 0) + 1
        return char_freq

    # Get the frequencies of each character in the text
    functools.reduce(getcharfreq, text, char_freq)

其中 text 是由许多字符组成的长字符串。 char_freq 是一个字典,键是字符,值是它在文本中的频率。

但是,我不确定如何在不先使用迭代的情况下获得字符双倍或三倍。例如,如果我的文本是“我是 Jimmy”,则双精度为:

“我”,

“我”,

"J',

“我”,

“我的”

三元组一次是每 3 个字符。迭代并将每 2 或 3 个字符添加到列表中,然后将其传递给 reduce() 将非常简单。但是由于我无法迭代,所以我对如何做到这一点有些茫然。

总的来说,我对 python 很陌生,对函数式编程的概念也很陌生,所以如果答案很简单,我深表歉意。此外,如果有完全不同的方法可以做到这一点,我们将不胜感激!

【问题讨论】:

  • getcharfreq 在函数式编程意义上并不是真正的“函数式”,它纯粹是为了副作用。
  • @juanpa.arrivillaga 我明白你的意思 - 我只知道如果我使用任何显式循环,我会受到严重惩罚,所以我正在努力寻找解决方法。
  • 那么您的约束究竟是什么?您必须使用reduce?无论如何,函数式编程中迭代循环的最通用替代方法是 recursion,这将是您可能必须生成所有单、对和三元组的方式
  • @juanpa.arrivillaga 我想reduce不是明确要求的,只是提到了:“使用map/reduce”。如果我在没有迭代的情况下这样做,我相信它会很好。递归,我明白了。我没想到。

标签: python dictionary functional-programming mapreduce reduce


【解决方案1】:

这是一个辅助函数,用于按顺序生成所有n 大小的分组。

def generate_n(seq, n, i, acc):
    if i >= len(seq):
        return acc
    else:
        acc.append(seq[i:i+n])
        return generate_n(seq, n, i + n, acc)

注意,它不是纯粹的函数式,因为我使用.append,但是没有它,你将不得不使用列表连接,这在这里效率非常低...一般来说,Python 不提供很多与典型的纯函数式编程结构一起工作的有效结构。但要“纯功能”,您可以使用:

def generate_n(seq, n, i, acc):
    if i >= len(seq):
        return acc
    else:
        return generate_n(seq, n, i + n, acc + [seq[i:i+n]])

另请注意,Python 不会优化尾递归,因此此解决方案受堆栈大小的限制(超过 1000 的深度会变得不可靠)。但这对于一个练习来说是可行的(而且你永远不会在生产 Python 中编写这样的代码)。

无论如何,它正在行动:

In [1]: def generate_n(seq, n, i, acc):
   ...:     if i >= len(seq):
   ...:         return acc
   ...:     else:
   ...:         acc.append(seq[i:i+n])
   ...:         return generate_n(seq, n, i + n, acc)
   ...:

In [2]: generate_n("I am Jimmy", 2, 0, [])
Out[2]: ['I ', 'am', ' J', 'im', 'my']

In [3]: generate_n("I am Jimmy", 3, 0, [])
Out[3]: ['I a', 'm J', 'imm', 'y']

In [4]: generate_n("I am Jimmy", 1, 0, [])
Out[4]: ['I', ' ', 'a', 'm', ' ', 'J', 'i', 'm', 'm', 'y']

然后解决方案归结为对每个 n 1->3 进行计数。

【讨论】:

    【解决方案2】:

    你可以使用递归吗?如果是这样,您可以使用递归函数和 map() 函数获取所有长度的元组集以生成计数列表:

    def prefixes(s):
        return [s] + prefixes(s[:-1]) if s else []
    
    def nTuples(s):
        return prefixes(s) + nTuples(s[1:]) if s else []
    
    def tupleCounts(s):
        tuples = nTuples(s)
        return list( map(lambda t:(t,tuples.count(t)), set(tuples)) )
    

    请注意,这会计算重叠元组(例如,ana 计数两次,因为它在 anana 中与自身重叠)

    如果您只想要 2 元组和 3 元组,您可以在分配给 tuples 变量的 tupleCounts() 中添加过滤器

    输出:

    tupleCounts("banana")
    
    [('a', 3),      ('ana', 2),   ('nan', 1),  ('nana', 1), ('bana', 1), 
     ('banana', 1), ('banan', 1), ('anan', 1), ('na', 2),   ('n', 2),
     ('an', 2),     ('ba', 1),    ('b', 1),    ('ban', 1),  ('anana', 1)]
    

    【讨论】:

      猜你喜欢
      • 2023-03-08
      • 2012-06-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多