To: vim_dev@googlegroups.com Subject: Patch 8.2.0878 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.0878 Problem: No reduce() function. Solution: Add a reduce() function. (closes #5481) Files: runtime/doc/eval.txt, src/evalfunc.c, src/globals.h, src/list.c, src/proto/list.pro, src/testdir/test_listdict.vim *** ../vim-8.2.0877/runtime/doc/eval.txt 2020-06-01 17:28:31.511939716 +0200 --- runtime/doc/eval.txt 2020-06-01 18:17:26.377235687 +0200 *************** *** 2673,2678 **** --- 2679,2686 ---- readdirex({dir} [, {expr}]) List file info in {dir} selected by {expr} readfile({fname} [, {type} [, {max}]]) List get list of lines from file {fname} + reduce({object}, {func} [, {initial}]) + any reduce {object} using {func} reg_executing() String get the executing register name reg_recording() String get the recording register name reltime([{start} [, {end}]]) List get time value *************** *** 7949,7954 **** --- 7967,7992 ---- Can also be used as a |method|: > GetFileName()->readfile() + reduce({object}, {func} [, {initial}]) *reduce()* *E998* + {func} is called for every item in {object}, which can be a + |List| or a |Blob|. {func} is called with two arguments: the + result so far and current item. After processing all items + the result is returned. + + {initial} is the initial result. When omitted, the first item + in {object} is used and {func} is first called for the second + item. If {initial} is not given and {object} is empty no + result can be computed, an E998 error is given. + + Examples: > + echo reduce([1, 3, 5], { acc, val -> acc + val }) + echo reduce(['x', 'y'], { acc, val -> acc .. val }, 'a') + echo reduce(0z1122, { acc, val -> 2 * acc + val }) + < + Can also be used as a |method|: > + echo mylist->reduce({ acc, val -> acc + val }, 0) + + reg_executing() *reg_executing()* Returns the single letter name of the register being executed. Returns an empty string when no register is being executed. *** ../vim-8.2.0877/src/evalfunc.c 2020-06-01 17:28:31.511939716 +0200 --- src/evalfunc.c 2020-06-01 17:46:19.838945213 +0200 *************** *** 769,774 **** --- 769,775 ---- {"readdir", 1, 2, FEARG_1, ret_list_string, f_readdir}, {"readdirex", 1, 2, FEARG_1, ret_list_dict_any, f_readdirex}, {"readfile", 1, 3, FEARG_1, ret_any, f_readfile}, + {"reduce", 2, 3, FEARG_1, ret_any, f_reduce}, {"reg_executing", 0, 0, 0, ret_string, f_reg_executing}, {"reg_recording", 0, 0, 0, ret_string, f_reg_recording}, {"reltime", 0, 2, FEARG_1, ret_list_any, f_reltime}, *** ../vim-8.2.0877/src/globals.h 2020-05-31 16:41:04.646603340 +0200 --- src/globals.h 2020-06-01 17:46:19.838945213 +0200 *************** *** 1690,1695 **** --- 1690,1696 ---- EXTERN char e_const_option[] INIT(= N_("E996: Cannot lock an option")); EXTERN char e_unknown_option[] INIT(= N_("E113: Unknown option: %s")); EXTERN char e_letunexp[] INIT(= N_("E18: Unexpected characters in :let")); + EXTERN char e_reduceempty[] INIT(= N_("E998: Reduce of an empty %s with no initial value")); #endif #ifdef FEAT_QUICKFIX EXTERN char e_readerrf[] INIT(= N_("E47: Error while reading errorfile")); *** ../vim-8.2.0877/src/list.c 2020-05-26 20:21:54.167611874 +0200 --- src/list.c 2020-06-01 18:36:56.572875472 +0200 *************** *** 2305,2308 **** --- 2305,2413 ---- } } + /* + * "reduce(list, { accumlator, element -> value } [, initial])" function + */ + void + f_reduce(typval_T *argvars, typval_T *rettv) + { + typval_T accum; + char_u *func_name; + partial_T *partial = NULL; + funcexe_T funcexe; + typval_T argv[3]; + + if (argvars[0].v_type != VAR_LIST && argvars[0].v_type != VAR_BLOB) + { + emsg(_(e_listblobreq)); + return; + } + + if (argvars[1].v_type == VAR_FUNC) + func_name = argvars[1].vval.v_string; + else if (argvars[1].v_type == VAR_PARTIAL) + { + partial = argvars[1].vval.v_partial; + func_name = partial_name(partial); + } + else + func_name = tv_get_string(&argvars[1]); + if (*func_name == NUL) + return; // type error or empty name + + vim_memset(&funcexe, 0, sizeof(funcexe)); + funcexe.evaluate = TRUE; + funcexe.partial = partial; + + if (argvars[0].v_type == VAR_LIST) + { + list_T *l = argvars[0].vval.v_list; + listitem_T *li = NULL; + + CHECK_LIST_MATERIALIZE(l); + if (argvars[2].v_type == VAR_UNKNOWN) + { + if (l == NULL || l->lv_first == NULL) + { + semsg(_(e_reduceempty), "List"); + return; + } + accum = l->lv_first->li_tv; + li = l->lv_first->li_next; + } + else + { + accum = argvars[2]; + if (l != NULL) + li = l->lv_first; + } + + copy_tv(&accum, rettv); + for ( ; li != NULL; li = li->li_next) + { + argv[0] = accum; + argv[1] = li->li_tv; + if (call_func(func_name, -1, rettv, 2, argv, &funcexe) == FAIL) + return; + accum = *rettv; + } + } + else + { + blob_T *b = argvars[0].vval.v_blob; + int i; + + if (argvars[2].v_type == VAR_UNKNOWN) + { + if (b == NULL || b->bv_ga.ga_len == 0) + { + semsg(_(e_reduceempty), "Blob"); + return; + } + accum.v_type = VAR_NUMBER; + accum.vval.v_number = blob_get(b, 0); + i = 1; + } + else + { + accum = argvars[2]; + i = 0; + } + + copy_tv(&accum, rettv); + if (b != NULL) + { + for ( ; i < b->bv_ga.ga_len; i++) + { + argv[0] = accum; + argv[1].v_type = VAR_NUMBER; + argv[1].vval.v_number = blob_get(b, i); + if (call_func(func_name, -1, rettv, 2, argv, &funcexe) == FAIL) + return; + accum = *rettv; + } + } + } + } + #endif // defined(FEAT_EVAL) *** ../vim-8.2.0877/src/proto/list.pro 2020-04-05 18:56:02.233436590 +0200 --- src/proto/list.pro 2020-06-01 17:46:19.838945213 +0200 *************** *** 51,54 **** --- 51,55 ---- void f_insert(typval_T *argvars, typval_T *rettv); void f_remove(typval_T *argvars, typval_T *rettv); void f_reverse(typval_T *argvars, typval_T *rettv); + void f_reduce(typval_T *argvars, typval_T *rettv); /* vim: set ft=c : */ *** ../vim-8.2.0877/src/testdir/test_listdict.vim 2020-04-25 15:24:40.551354115 +0200 --- src/testdir/test_listdict.vim 2020-06-01 17:53:56.392574568 +0200 *************** *** 680,685 **** --- 680,716 ---- call assert_fails("call sort([1, 2], function('min'))", "E702:") endfunc + " reduce a list or a blob + func Test_reduce() + call assert_equal(1, reduce([], { acc, val -> acc + val }, 1)) + call assert_equal(10, reduce([1, 3, 5], { acc, val -> acc + val }, 1)) + call assert_equal(2 * (2 * ((2 * 1) + 2) + 3) + 4, reduce([2, 3, 4], { acc, val -> 2 * acc + val }, 1)) + call assert_equal('a x y z', ['x', 'y', 'z']->reduce({ acc, val -> acc .. ' ' .. val}, 'a')) + call assert_equal(#{ x: 1, y: 1, z: 1 }, ['x', 'y', 'z']->reduce({ acc, val -> extend(acc, { val: 1 }) }, {})) + call assert_equal([0, 1, 2, 3], reduce([1, 2, 3], function('add'), [0])) + + let l = ['x', 'y', 'z'] + call assert_equal(42, reduce(l, function('get'), #{ x: #{ y: #{ z: 42 } } })) + call assert_equal(['x', 'y', 'z'], l) + + call assert_equal(1, reduce([1], { acc, val -> acc + val })) + call assert_equal('x y z', reduce(['x', 'y', 'z'], { acc, val -> acc .. ' ' .. val })) + call assert_equal(120, range(1, 5)->reduce({ acc, val -> acc * val })) + call assert_fails("call reduce([], { acc, val -> acc + val })", 'E998: Reduce of an empty List with no initial value') + + call assert_equal(1, reduce(0z, { acc, val -> acc + val }, 1)) + call assert_equal(1 + 0xaf + 0xbf + 0xcf, reduce(0zAFBFCF, { acc, val -> acc + val }, 1)) + call assert_equal(2 * (2 * 1 + 0xaf) + 0xbf, 0zAFBF->reduce({ acc, val -> 2 * acc + val }, 1)) + + call assert_equal(0xff, reduce(0zff, { acc, val -> acc + val })) + call assert_equal(2 * (2 * 0xaf + 0xbf) + 0xcf, reduce(0zAFBFCF, { acc, val -> 2 * acc + val })) + call assert_fails("call reduce(0z, { acc, val -> acc + val })", 'E998: Reduce of an empty Blob with no initial value') + + call assert_fails("call reduce({}, { acc, val -> acc + val }, 1)", 'E897:') + call assert_fails("call reduce(0, { acc, val -> acc + val }, 1)", 'E897:') + call assert_fails("call reduce('', { acc, val -> acc + val }, 1)", 'E897:') + endfunc + " splitting a string to a List using split() func Test_str_split() call assert_equal(['aa', 'bb'], split(' aa bb ')) *** ../vim-8.2.0877/src/version.c 2020-06-01 17:28:31.515939699 +0200 --- src/version.c 2020-06-01 18:37:25.180752064 +0200 *************** *** 748,749 **** --- 748,751 ---- { /* Add new patch number below this line */ + /**/ + 878, /**/ -- hundred-and-one symptoms of being an internet addict: 243. You unsuccessfully try to download a pizza from www.dominos.com. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///