Skip to content

Commit f8ca03b

Browse files
committed
patch 8.2.2065: using map() and filter() on a range() is inefficient
Problem: Using map() and filter() on a range() is inefficient. Solution: Do not materialize the range. (closes #7388)
1 parent ebec3e2 commit f8ca03b

3 files changed

Lines changed: 85 additions & 27 deletions

File tree

src/list.c

Lines changed: 79 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2173,43 +2173,95 @@ filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap)
21732173
// set_vim_var_nr() doesn't set the type
21742174
set_vim_var_type(VV_KEY, VAR_NUMBER);
21752175

2176-
CHECK_LIST_MATERIALIZE(l);
21772176
if (filtermap != FILTERMAP_FILTER && l->lv_lock == 0)
21782177
l->lv_lock = VAR_LOCKED;
2179-
for (li = l->lv_first; li != NULL; li = nli)
2178+
2179+
if (l->lv_first == &range_list_item)
21802180
{
2181-
typval_T newtv;
2181+
varnumber_T val = l->lv_u.nonmat.lv_start;
2182+
int len = l->lv_len;
2183+
int stride = l->lv_u.nonmat.lv_stride;
21822184

2183-
if (filtermap != FILTERMAP_FILTER
2184-
&& value_check_lock(li->li_tv.v_lock, arg_errmsg, TRUE))
2185-
break;
2186-
nli = li->li_next;
2187-
set_vim_var_nr(VV_KEY, idx);
2188-
if (filter_map_one(&li->li_tv, expr, filtermap,
2189-
&newtv, &rem) == FAIL)
2190-
break;
2191-
if (did_emsg)
2192-
{
2193-
clear_tv(&newtv);
2194-
break;
2195-
}
2196-
if (filtermap == FILTERMAP_MAP)
2185+
// List from range(): loop over the numbers
2186+
l->lv_first = NULL;
2187+
l->lv_u.mat.lv_last = NULL;
2188+
l->lv_len = 0;
2189+
l->lv_u.mat.lv_idx_item = NULL;
2190+
2191+
for (idx = 0; idx < len; ++idx)
21972192
{
2198-
// map(): replace the list item value
2199-
clear_tv(&li->li_tv);
2200-
newtv.v_lock = 0;
2201-
li->li_tv = newtv;
2193+
typval_T tv;
2194+
typval_T newtv;
2195+
2196+
tv.v_type = VAR_NUMBER;
2197+
tv.v_lock = 0;
2198+
tv.vval.v_number = val;
2199+
set_vim_var_nr(VV_KEY, idx);
2200+
if (filter_map_one(&tv, expr, filtermap, &newtv, &rem)
2201+
== FAIL)
2202+
break;
2203+
if (did_emsg)
2204+
{
2205+
clear_tv(&newtv);
2206+
break;
2207+
}
2208+
if (filtermap != FILTERMAP_FILTER)
2209+
{
2210+
// map(), mapnew(): always append the new value to the
2211+
// list
2212+
if (list_append_tv_move(filtermap == FILTERMAP_MAP
2213+
? l : l_ret, &newtv) == FAIL)
2214+
break;
2215+
}
2216+
else if (!rem)
2217+
{
2218+
// filter(): append the list item value when not rem
2219+
if (list_append_tv_move(l, &tv) == FAIL)
2220+
break;
2221+
}
2222+
2223+
val += stride;
22022224
}
2203-
else if (filtermap == FILTERMAP_MAPNEW)
2225+
}
2226+
else
2227+
{
2228+
// Materialized list from range(): loop over the items
2229+
for (li = l->lv_first; li != NULL; li = nli)
22042230
{
2205-
// mapnew(): append the list item value
2206-
if (list_append_tv_move(l_ret, &newtv) == FAIL)
2231+
typval_T newtv;
2232+
2233+
if (filtermap != FILTERMAP_FILTER && value_check_lock(
2234+
li->li_tv.v_lock, arg_errmsg, TRUE))
2235+
break;
2236+
nli = li->li_next;
2237+
set_vim_var_nr(VV_KEY, idx);
2238+
if (filter_map_one(&li->li_tv, expr, filtermap,
2239+
&newtv, &rem) == FAIL)
22072240
break;
2241+
if (did_emsg)
2242+
{
2243+
clear_tv(&newtv);
2244+
break;
2245+
}
2246+
if (filtermap == FILTERMAP_MAP)
2247+
{
2248+
// map(): replace the list item value
2249+
clear_tv(&li->li_tv);
2250+
newtv.v_lock = 0;
2251+
li->li_tv = newtv;
2252+
}
2253+
else if (filtermap == FILTERMAP_MAPNEW)
2254+
{
2255+
// mapnew(): append the list item value
2256+
if (list_append_tv_move(l_ret, &newtv) == FAIL)
2257+
break;
2258+
}
2259+
else if (filtermap == FILTERMAP_FILTER && rem)
2260+
listitem_remove(l, li);
2261+
++idx;
22082262
}
2209-
else if (filtermap == FILTERMAP_FILTER && rem)
2210-
listitem_remove(l, li);
2211-
++idx;
22122263
}
2264+
22132265
l->lv_lock = prev_lock;
22142266
}
22152267

src/testdir/test_functions.vim

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2302,6 +2302,7 @@ func Test_range()
23022302

23032303
" filter()
23042304
call assert_equal([1, 3], filter(range(5), 'v:val % 2'))
2305+
call assert_equal([1, 5, 7, 11, 13], filter(filter(range(15), 'v:val % 2'), 'v:val % 3'))
23052306

23062307
" funcref()
23072308
call assert_equal([0, 1], funcref('TwoArgs', range(2))())
@@ -2358,6 +2359,9 @@ func Test_range()
23582359

23592360
" map()
23602361
call assert_equal([0, 2, 4, 6, 8], map(range(5), 'v:val * 2'))
2362+
call assert_equal([3, 5, 7, 9, 11], map(map(range(5), 'v:val * 2'), 'v:val + 3'))
2363+
call assert_equal([2, 6], map(filter(range(5), 'v:val % 2'), 'v:val * 2'))
2364+
call assert_equal([2, 4, 8], filter(map(range(5), 'v:val * 2'), 'v:val % 3'))
23612365

23622366
" match()
23632367
call assert_equal(3, match(range(5), 3))

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,8 @@ static char *(features[]) =
750750

751751
static int included_patches[] =
752752
{ /* Add new patch number below this line */
753+
/**/
754+
2065,
753755
/**/
754756
2064,
755757
/**/

0 commit comments

Comments
 (0)