Skip to content

Commit 0f4d26c

Browse files
committed
CHANGE: REMOVE-EACH returns the modified series at the argument position as in Rebol2
Added a `REMOVE-EACH/count` refinement, which toggles `REMOVE-EACH` to returning the removal count. resolves: Oldes/Rebol-issues#931
1 parent 516dfee commit 0f4d26c

File tree

4 files changed

+72
-25
lines changed

4 files changed

+72
-25
lines changed

src/boot/natives.reb

+2-1
Original file line numberDiff line numberDiff line change
@@ -270,10 +270,11 @@ repeat: native [
270270
]
271271

272272
remove-each: native [
273-
{Removes values for each block that returns true; returns removal count.}
273+
{Removes values for each block that returns truthy value.}
274274
'word [word! block!] {Word or block of words to set each time (local)}
275275
data [series! map!] {The series to traverse (modified)}
276276
body [block!] {Block to evaluate (return TRUE to remove)}
277+
/count {Returns removal count}
277278
]
278279

279280
return: native [

src/core/n-loop.c

+40-23
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@
3131
#include "sys-core.h"
3232
#include "sys-int-funcs.h" //REB_I64_ADD_OF
3333

34+
enum loop_each_mode {
35+
LM_FOR = 0,
36+
LM_REMOVE,
37+
LM_REMOVE_COUNT,
38+
LM_MAP
39+
};
3440

3541
/***********************************************************************
3642
**
@@ -271,15 +277,15 @@
271277
return R_RET;
272278
}
273279

274-
275280
/***********************************************************************
276281
**
277282
*/ static int Loop_Each(REBVAL *ds, REBINT mode)
278283
/*
279284
** Supports these natives (modes):
280285
** 0: foreach
281286
** 1: remove-each
282-
** 2: map
287+
** 2: remove-each/count
288+
** 3: map-each
283289
**
284290
***********************************************************************/
285291
{
@@ -289,7 +295,7 @@
289295
REBSER *frame;
290296
REBVAL *value;
291297
REBSER *series;
292-
REBSER *out = NULL; // output block (for MAP, mode = 2)
298+
REBSER *out = NULL; // output block (for LM_MAP, mode = 2)
293299

294300
REBINT index; // !!!! should these be REBCNT?
295301
REBINT tail;
@@ -298,8 +304,9 @@
298304
REBINT err;
299305
REBCNT i;
300306
REBCNT j;
307+
REBOOL return_count = FALSE;
301308

302-
ASSERT2(mode >= 0 && mode < 3, RP_MISC);
309+
ASSERT2(mode >= 0 && mode < 4, RP_MISC);
303310

304311
value = D_ARG(2); // series
305312
if (IS_NONE(value)) return R_NONE;
@@ -311,11 +318,15 @@
311318
SET_NONE(D_RET);
312319
SET_NONE(DS_NEXT);
313320

314-
// If it's MAP, create result block:
315-
if (mode == 2) {
321+
// If it's `map-each`, create result block:
322+
if (mode == LM_MAP) {
316323
out = Make_Block(VAL_LEN(value));
317324
Set_Block(D_RET, out);
318325
}
326+
else if (mode == LM_REMOVE_COUNT) {
327+
mode = LM_REMOVE;
328+
return_count = TRUE;
329+
}
319330

320331
// Get series info:
321332
if (ANY_OBJECT(value)) {
@@ -333,14 +344,16 @@
333344
series = VAL_SERIES(value);
334345
index = VAL_INDEX(value);
335346
if (index >= (REBINT)SERIES_TAIL(series)) {
336-
if (mode == 1) {
337-
SET_INTEGER(D_RET, 0);
347+
if (mode == LM_REMOVE) {
348+
if(return_count)
349+
SET_INTEGER(D_RET, 0);
350+
else return R_ARG2;
338351
}
339352
return R_RET;
340353
}
341354
}
342355

343-
if (mode==1 && IS_PROTECT_SERIES(series))
356+
if (mode==LM_REMOVE && IS_PROTECT_SERIES(series))
344357
Trap0(RE_PROTECTED);
345358

346359
windex = index;
@@ -448,16 +461,16 @@
448461
break;
449462
}
450463
// else CONTINUE:
451-
if (mode == 1) SET_FALSE(ds); // keep the value (for mode == 1)
464+
if (mode == LM_REMOVE) SET_FALSE(ds); // keep the value (for mode == LM_REMOVE)
452465
} else {
453466
err = 0; // prevent later test against uninitialized value
454467
}
455468

456-
if (mode > 0) {
469+
if (mode > LM_FOR) {
457470
//if (ANY_OBJECT(value)) Trap_Types(words, REB_BLOCK, VAL_TYPE(value)); //check not needed
458471

459472
// If FALSE return, copy values to the write location:
460-
if (mode == 1) { // remove-each
473+
if (mode == LM_REMOVE) { // remove-each
461474
if (IS_FALSE(ds)) {
462475
REBCNT wide = SERIES_WIDE(series);
463476
// memory areas may overlap, so use memmove and not memcpy!
@@ -467,24 +480,27 @@
467480
}
468481
}
469482
else
470-
if (!IS_UNSET(ds)) Append_Val(out, ds); // (mode == 2)
483+
if (!IS_UNSET(ds)) Append_Val(out, ds); // (mode == LM_MAP)
471484
}
472485
skip_hidden: ;
473486
}
474487

475488
// Finish up:
476-
if (mode == 1) {
489+
if (mode == LM_REMOVE) {
477490
// Remove hole (updates tail):
478491
if (windex < index) Remove_Series(series, windex, index - windex);
479-
SET_INTEGER(DS_RETURN, index - windex);
480-
if (IS_MAP(value)) return R_ARG2;
481-
return R_RET;
492+
if (return_count) {
493+
index -= windex;
494+
SET_INTEGER(DS_RETURN, IS_MAP(value) ? index / 2 : index);
495+
return R_RET;
496+
}
497+
return R_ARG2;
482498
}
483499

484-
// If MAP and not BREAK/RETURN:
485-
if (mode == 2 && err != 2) return R_RET;
500+
// If map-each and not BREAK/RETURN:
501+
if (mode == LM_MAP && err != 2) return R_RET;
486502

487-
return R_TOS1;
503+
return R_TOS1; // foreach
488504
}
489505

490506

@@ -586,7 +602,7 @@ skip_hidden: ;
586602
**
587603
***********************************************************************/
588604
{
589-
return Loop_Each(ds, 0);
605+
return Loop_Each(ds, LM_FOR);
590606
}
591607

592608

@@ -597,10 +613,11 @@ skip_hidden: ;
597613
** 'word [get-word! word! block!] {Word or block of words}
598614
** data [series!] {The series to traverse}
599615
** body [block!] {Block to evaluate each time}
616+
** /count
600617
**
601618
***********************************************************************/
602619
{
603-
return Loop_Each(ds, 1);
620+
return Loop_Each(ds, D_REF(4) ? LM_REMOVE_COUNT : LM_REMOVE);
604621
}
605622

606623

@@ -614,7 +631,7 @@ skip_hidden: ;
614631
**
615632
***********************************************************************/
616633
{
617-
return Loop_Each(ds, 2);
634+
return Loop_Each(ds, LM_MAP);
618635
}
619636

620637

src/tests/units/map-test.r3

+4
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,10 @@ Rebol [
313313
m: #(a 1 "b" 2 c #[none] d: 3)
314314
--assert m = remove-each [k v] m [any [string? k none? v]]
315315
--assert [a d] = words-of m
316+
--test-- "remove-each/count with map"
317+
m: #(a 1 "b" 2 c #[none] d: 3)
318+
--assert 2 = remove-each/count [k v] m [any [string? k none? v]]
319+
--assert [a d] = words-of m
316320

317321
===end-group===
318322

src/tests/units/series-test.r3

+26-1
Original file line numberDiff line numberDiff line change
@@ -950,14 +950,39 @@ Rebol [
950950
data: copy ""
951951
foreach x "123" [append data x]
952952
--assert "123" = data
953+
--test-- "FOREACH result"
954+
--assert #"3" = foreach x "123" [x]
955+
--assert 3 = foreach x [1 2 3] [x]
956+
--assert unset? foreach x [1 2 3] [if x = 2 [break]]
957+
--assert 4 = foreach x [1 2 3] [if x = 2 [break/return 4]]
953958

954959
===end-group===
955960

956961
===start-group=== "REMOVE-EACH"
962+
--test-- "remove-each result"
963+
;@@ https://github.com/Oldes/Rebol-issues/issues/931
964+
b: [a 1 b 2]
965+
--assert [b 2] = remove-each [k v] b [v < 2]
966+
--assert [b 2] = b
967+
968+
s: next [1 2 3 4]
969+
--assert [3 4] = remove-each n s [n < 3]
970+
--assert [1 3 4] = head s
971+
972+
--test-- "remove-each/count result"
973+
b: [a 1 b 2]
974+
--assert 2 = remove-each/count [k v] b [v < 2]
975+
--assert b = [b 2]
976+
977+
s: next [1 2 3 4]
978+
--assert 1 = remove-each/count n s [n < 3]
979+
--assert [1 3 4] = head s
957980

958981
--test-- "break in remove-each"
959982
;@@ https://github.com/Oldes/Rebol-issues/issues/2192
960-
remove-each n s: [1 2 3 4] [if n = 2 [break] true]
983+
--assert [2 3 4] = remove-each n s: [1 2 3 4] [if n = 2 [break] true]
984+
--assert s = [2 3 4]
985+
--assert 1 = remove-each/count n s: [1 2 3 4] [if n = 2 [break] true]
961986
--assert s = [2 3 4]
962987

963988
===end-group===

0 commit comments

Comments
 (0)