10

Vim 脚本有一些非常基本的函数式编程工具。

它有map()and filter(),但据我所知,它缺少一个reduce()功能。“ Reduce ”将一组值减少为单个值。

有没有办法reduce()在 Vim 脚本中以某种方式创建或模拟它?是否可以在 Vim 脚本表达式中减少值列表,而无需编写显式循环?例如,有没有办法减少加法运算的前五个正整数,就像函数式语言课程的 par 一样?

在 JavaScript 中:

[1, 2, 3, 4, 5].reduce(function(x, y) { return x + y; });
15

在 Clojure 中:

(reduce + (range 1 (inc 5)))
15

在哈斯克尔:

foldl (+) 0 [1..5]
15

在 J 中:

+/>:i.5
15

在 Vim 脚本中:...?

4

3 回答 3

7

供将来参考,这是我对主题的变体,灵感来自@MatthewStrawbridge 链接的答案。

原始示例问题的表达式:

eval(join(range(1, 5), '+'))

一个更通用的解决方案,使用Add(),其中arange(1, 5)

eval(repeat('Add(',len(a)-1).a[0].','.join(a[1:],'),').')')

这构造了 string "Add(Add(Add(Add(1,2),3),4),5)",然后evals 它。乐趣!

最后,Reduce(), 它接受一个 Funcref 和一个列表,然后使用 Vim 的列表“解构”语法在一个循环中减少它[x, y; z]。见:h :let-unpack

function! Reduce(f, list)
  let [acc; tail] = a:list
  while !empty(tail)
    let [head; tail] = tail
    let acc = a:f(acc, head)
  endwhile
  return acc
endfunction

这就是它的使用方式:

:echo Reduce(function('Add'), range(1, 5))
15
于 2013-09-15T11:59:12.863 回答
5

我认为您应该构造一个字符串然后执行它(我承认这感觉有点笨拙)。帮助 ( :h E714) 给出了这个例子:

:exe 'let sum = ' . join(nrlist, '+')

所以在你的情况下, where nrlistis [1, 2, 3, 4, 5],它将构造字符串let sum = 1+2+3+4+5然后执行它。

或者,您可以编写自己的 reduce 函数,因为没有内置函数。

编辑:

我在 vim_use Google Group 上找到了关于Vim 中的函数式编程的讨论( How strong is language build in vim compare with the language build in emacs?,2010 年 1 月 25 日),其中包括几个这样的 reduce 函数的实现。

第一个由 Tom Link 撰写,如下:

function! Reduce(ffn, list) "{{{3
    if empty(a:list)
        return ''
    else
        let list = copy(a:list)
        let s:acc = remove(list, 0)
        let ffn = substitute(a:ffn, '\<v:acc\>', "s:acc", 'g')
        for val in list
            let s:acc = eval(substitute(ffn, '\<v:val\>', val, 'g'))
        endfor
        return s:acc
    endif
endf


echom Reduce("v:val + v:acc", [1, 2, 3, 4])
echom Reduce("v:val > v:acc ? v:val : v:acc", [1, 2, 3, 4])
echom Reduce("'v:val' < v:acc ? 'v:val' : v:acc", split("characters",
'\zs'))

第二个,安东尼斯克里文,如下:

fun Reduce(funcname, list)
    let F = function(a:funcname)
    let acc = a:list[0]
    for value in a:list[1:]
        let acc = F(acc, value)
    endfor
    return acc
endfun

fun Add(a,b)
    return a:a + a:b
endfun

fun Max(a,b)
    return a:a > a:b ? a:a : a:b
endfun

fun Min(a,b)
    return a:a < a:b ? a:a : a:b
endfun

let list = [1,2,3,4,5]
echo Reduce('Add', list)
echo Reduce('Max', list)
echo Reduce('Min', list)
于 2013-09-15T09:47:21.967 回答
0

很遗憾它没有reduce功能,这是我的

function s:reduce(acc, fn, args) abort
  let acc = a:acc
  for item in a:args
    let acc = a:fn(a:acc, item)
  endfor
  return acc
endfunc

这是根据 reduce 定义的 sum 函数

let Sum = {... -> s:reduce(0, {acc, arg -> acc + arg}, a:000)}
于 2021-05-14T23:37:04.240 回答