]> granicus.if.org Git - vim/commitdiff
patch 8.2.2065: using map() and filter() on a range() is inefficient v8.2.2065
authorBram Moolenaar <Bram@vim.org>
Sat, 28 Nov 2020 19:32:29 +0000 (20:32 +0100)
committerBram Moolenaar <Bram@vim.org>
Sat, 28 Nov 2020 19:32:29 +0000 (20:32 +0100)
Problem:    Using map() and filter() on a range() is inefficient.
Solution:   Do not materialize the range. (closes #7388)

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

index e75a2a7413fff3d66c1582bccee05ec1e494a4e0..c364fa72fda590d0dee21501d7a2c817e93faf63 100644 (file)
@@ -2173,43 +2173,95 @@ filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap)
            // set_vim_var_nr() doesn't set the type
            set_vim_var_type(VV_KEY, VAR_NUMBER);
 
-           CHECK_LIST_MATERIALIZE(l);
            if (filtermap != FILTERMAP_FILTER && l->lv_lock == 0)
                l->lv_lock = VAR_LOCKED;
-           for (li = l->lv_first; li != NULL; li = nli)
+
+           if (l->lv_first == &range_list_item)
            {
-               typval_T newtv;
+               varnumber_T     val = l->lv_u.nonmat.lv_start;
+               int             len = l->lv_len;
+               int             stride = l->lv_u.nonmat.lv_stride;
 
-               if (filtermap != FILTERMAP_FILTER
-                      && value_check_lock(li->li_tv.v_lock, arg_errmsg, TRUE))
-                   break;
-               nli = li->li_next;
-               set_vim_var_nr(VV_KEY, idx);
-               if (filter_map_one(&li->li_tv, expr, filtermap,
-                                                        &newtv, &rem) == FAIL)
-                   break;
-               if (did_emsg)
-               {
-                   clear_tv(&newtv);
-                   break;
-               }
-               if (filtermap == FILTERMAP_MAP)
+               // List from range(): loop over the numbers
+               l->lv_first = NULL;
+               l->lv_u.mat.lv_last = NULL;
+               l->lv_len = 0;
+               l->lv_u.mat.lv_idx_item = NULL;
+
+               for (idx = 0; idx < len; ++idx)
                {
-                   // map(): replace the list item value
-                   clear_tv(&li->li_tv);
-                   newtv.v_lock = 0;
-                   li->li_tv = newtv;
+                   typval_T tv;
+                   typval_T newtv;
+
+                   tv.v_type = VAR_NUMBER;
+                   tv.v_lock = 0;
+                   tv.vval.v_number = val;
+                   set_vim_var_nr(VV_KEY, idx);
+                   if (filter_map_one(&tv, expr, filtermap, &newtv, &rem)
+                                                                      == FAIL)
+                       break;
+                   if (did_emsg)
+                   {
+                       clear_tv(&newtv);
+                       break;
+                   }
+                   if (filtermap != FILTERMAP_FILTER)
+                   {
+                       // map(), mapnew(): always append the new value to the
+                       // list
+                       if (list_append_tv_move(filtermap == FILTERMAP_MAP
+                                                 ? l : l_ret, &newtv) == FAIL)
+                           break;
+                   }
+                   else if (!rem)
+                   {
+                       // filter(): append the list item value when not rem
+                       if (list_append_tv_move(l, &tv) == FAIL)
+                           break;
+                   }
+
+                   val += stride;
                }
-               else if (filtermap == FILTERMAP_MAPNEW)
+           }
+           else
+           {
+               // Materialized list from range(): loop over the items
+               for (li = l->lv_first; li != NULL; li = nli)
                {
-                   // mapnew(): append the list item value
-                   if (list_append_tv_move(l_ret, &newtv) == FAIL)
+                   typval_T newtv;
+
+                   if (filtermap != FILTERMAP_FILTER && value_check_lock(
+                                          li->li_tv.v_lock, arg_errmsg, TRUE))
+                       break;
+                   nli = li->li_next;
+                   set_vim_var_nr(VV_KEY, idx);
+                   if (filter_map_one(&li->li_tv, expr, filtermap,
+                               &newtv, &rem) == FAIL)
                        break;
+                   if (did_emsg)
+                   {
+                       clear_tv(&newtv);
+                       break;
+                   }
+                   if (filtermap == FILTERMAP_MAP)
+                   {
+                       // map(): replace the list item value
+                       clear_tv(&li->li_tv);
+                       newtv.v_lock = 0;
+                       li->li_tv = newtv;
+                   }
+                   else if (filtermap == FILTERMAP_MAPNEW)
+                   {
+                       // mapnew(): append the list item value
+                       if (list_append_tv_move(l_ret, &newtv) == FAIL)
+                           break;
+                   }
+                   else if (filtermap == FILTERMAP_FILTER && rem)
+                       listitem_remove(l, li);
+                   ++idx;
                }
-               else if (filtermap == FILTERMAP_FILTER && rem)
-                   listitem_remove(l, li);
-               ++idx;
            }
+
            l->lv_lock = prev_lock;
        }
 
index fb0b1936585c3fc15e3cf3257c4debe3e548a10f..d6e408a8433b7bcae5f875e98d0afdd4fd506078 100644 (file)
@@ -2302,6 +2302,7 @@ func Test_range()
 
   " filter()
   call assert_equal([1, 3], filter(range(5), 'v:val % 2'))
+  call assert_equal([1, 5, 7, 11, 13], filter(filter(range(15), 'v:val % 2'), 'v:val % 3'))
 
   " funcref()
   call assert_equal([0, 1], funcref('TwoArgs', range(2))())
@@ -2358,6 +2359,9 @@ func Test_range()
 
   " map()
   call assert_equal([0, 2, 4, 6, 8], map(range(5), 'v:val * 2'))
+  call assert_equal([3, 5, 7, 9, 11], map(map(range(5), 'v:val * 2'), 'v:val + 3'))
+  call assert_equal([2, 6], map(filter(range(5), 'v:val % 2'), 'v:val * 2'))
+  call assert_equal([2, 4, 8], filter(map(range(5), 'v:val * 2'), 'v:val % 3'))
 
   " match()
   call assert_equal(3, match(range(5), 3))
index ec0fabc3ee2b1c4cb03d74b798c6844401865004..dac3d5cf718b04f07e10a9aa526bef5ac0ed9b28 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2065,
 /**/
     2064,
 /**/