]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.0.0615: using reduce() on a list from range() is a bit slow v9.0.0615
authorBram Moolenaar <Bram@vim.org>
Wed, 28 Sep 2022 12:22:59 +0000 (13:22 +0100)
committerBram Moolenaar <Bram@vim.org>
Wed, 28 Sep 2022 12:22:59 +0000 (13:22 +0100)
Problem:    Using reduce() on a list from range() is a bit slow.
Solution:   Avoid materializing the list.

src/list.c
src/testdir/test_listdict.vim
src/version.c

index a7b21893b3fc736b1be0225bd3fb75ca88871c75..fab86fd752c0a1438aec83739f9ef1195d6d99b7 100644 (file)
@@ -2377,9 +2377,8 @@ list_filter_map(
        rettv->v_type = VAR_LIST;
        rettv->vval.v_list = NULL;
     }
-    if (l == NULL
-           || (filtermap == FILTERMAP_FILTER
-               && value_check_lock(l->lv_lock, arg_errmsg, TRUE)))
+    if (l == NULL || (filtermap == FILTERMAP_FILTER
+                           && value_check_lock(l->lv_lock, arg_errmsg, TRUE)))
        return;
 
     prev_lock = l->lv_lock;
@@ -3011,28 +3010,44 @@ list_reduce(
 {
     list_T     *l = argvars[0].vval.v_list;
     listitem_T  *li = NULL;
+    int                range_list;
+    int                range_idx = 0;
+    varnumber_T        range_val = 0;
     typval_T   initial;
     typval_T   argv[3];
     int                r;
     int                called_emsg_start = called_emsg;
     int                prev_locked;
 
-    if (l != NULL)
-       CHECK_LIST_MATERIALIZE(l);
+    // Using reduce on a range() uses "range_idx" and "range_val".
+    range_list = l != NULL && l->lv_first == &range_list_item;
+    if (range_list)
+       range_val = l->lv_u.nonmat.lv_start;
+
     if (argvars[2].v_type == VAR_UNKNOWN)
     {
-       if (l == NULL || l->lv_first == NULL)
+       if (l == NULL || l->lv_len == 0)
        {
            semsg(_(e_reduce_of_an_empty_str_with_no_initial_value), "List");
            return;
        }
-       initial = l->lv_first->li_tv;
-       li = l->lv_first->li_next;
+       if (range_list)
+       {
+           initial.v_type = VAR_NUMBER;
+           initial.vval.v_number = range_val;
+           range_val += l->lv_u.nonmat.lv_stride;
+           range_idx = 1;
+       }
+       else
+       {
+           initial = l->lv_first->li_tv;
+           li = l->lv_first->li_next;
+       }
     }
     else
     {
        initial = argvars[2];
-       if (l != NULL)
+       if (l != NULL && !range_list)
            li = l->lv_first;
     }
     copy_tv(&initial, rettv);
@@ -3041,20 +3056,36 @@ list_reduce(
        return;
 
     prev_locked = l->lv_lock;
-
     l->lv_lock = VAR_FIXED;  // disallow the list changing here
-    for ( ; li != NULL; li = li->li_next)
+
+    while (range_list ? range_idx < l->lv_len : li != NULL)
     {
        argv[0] = *rettv;
-       argv[1] = li->li_tv;
        rettv->v_type = VAR_UNKNOWN;
 
+       if (range_list)
+       {
+           argv[1].v_type = VAR_NUMBER;
+           argv[1].vval.v_number = range_val;
+       }
+       else
+           argv[1] = li->li_tv;
+
        r = eval_expr_typval(expr, argv, 2, rettv);
 
        clear_tv(&argv[0]);
        if (r == FAIL || called_emsg != called_emsg_start)
            break;
+
+       if (range_list)
+       {
+           range_val += l->lv_u.nonmat.lv_stride;
+           ++range_idx;
+       }
+       else
+           li = li->li_next;
     }
+
     l->lv_lock = prev_locked;
 }
 
index 7c707d46aaeebffe70983e712e301c47cdadaf2b..168ef077d88240073808c2c793b74f2c6be98754 100644 (file)
@@ -1015,6 +1015,12 @@ func Test_reduce()
       call assert_equal('x y z', reduce(['x', 'y', 'z'], LSTART acc, val LMIDDLE acc .. ' ' .. val LEND))
       call assert_equal(120, range(1, 5)->reduce(LSTART acc, val LMIDDLE acc * val LEND))
 
+      call assert_equal(0, range(1)->reduce(LSTART acc, val LMIDDLE acc + val LEND))
+      call assert_equal(1, range(2)->reduce(LSTART acc, val LMIDDLE acc + val LEND))
+      call assert_equal(3, range(3)->reduce(LSTART acc, val LMIDDLE acc + val LEND))
+      call assert_equal(6, range(4)->reduce(LSTART acc, val LMIDDLE acc + val LEND))
+      call assert_equal(10, range(5)->reduce(LSTART acc, val LMIDDLE acc + val LEND))
+
       call assert_equal(1, reduce(0z, LSTART acc, val LMIDDLE acc + val LEND, 1))
       call assert_equal(1 + 0xaf + 0xbf + 0xcf, reduce(0zAFBFCF, LSTART acc, val LMIDDLE acc + val LEND, 1))
       call assert_equal(2 * (2 * 1 + 0xaf) + 0xbf, 0zAFBF->reduce(LSTART acc, val LMIDDLE 2 * acc + val LEND, 1))
@@ -1038,6 +1044,7 @@ func Test_reduce()
   vim9 assert_equal({'x': 1, 'y': 1, 'z': 1 }, ['x', 'y', 'z']->reduce((acc, val) => extend(acc, {[val]: 1 }), {}))
 
   call assert_fails("call reduce([], { acc, val -> acc + val })", 'E998: Reduce of an empty List with no initial value')
+  call assert_fails("call reduce(range(0), { acc, val -> acc + val })", 'E998: Reduce of an empty List with no initial value')
   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(test_null_blob(), { acc, val -> acc + val })", 'E998: Reduce of an empty Blob with no initial value')
   call assert_fails("call reduce('', { acc, val -> acc + val })", 'E998: Reduce of an empty String with no initial value')
index 623fedaf12746e1ab89d3a18db5f5919a9a96a6f..5bf2999ae16118a1ba17a0e5b64c41b4e49e4292 100644 (file)
@@ -699,6 +699,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    615,
 /**/
     614,
 /**/