Skip to content

Commit 545e090

Browse files
committed
Add FoldLeft, FoldLeftOp, FoldLeftX
1 parent a573b34 commit 545e090

File tree

6 files changed

+282
-3
lines changed

6 files changed

+282
-3
lines changed

lib/coll.gd

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2451,6 +2451,52 @@ DeclareOperation( "AsSSortedListNonstored", [IsListOrCollection] );
24512451
DeclareGlobalFunction( "Elements" );
24522452

24532453

2454+
#############################################################################
2455+
##
2456+
#O FoldLeft( <C>, <func>[, <init>] )
2457+
##
2458+
## <ManSection>
2459+
## <Func Name="FoldLeft" Arg='C, func[, init]'/>
2460+
##
2461+
## <Description>
2462+
## TODO
2463+
## </Description>
2464+
## </ManSection>
2465+
##
2466+
## TODO: explain in docs that to imitate the behavior of
2467+
## x := Sum(list, func, init);
2468+
## one can do this:
2469+
## x := FoldLeft(list, {x,y} -> x + func(y), func(init));
2470+
## or of course also this (but requires more memory)
2471+
## x := FoldLeft(List(list, func), \+);
2472+
##
2473+
## There is no good way to imitate `Sum(list, func)` without
2474+
## an initial value.
2475+
##
2476+
DeclareGlobalFunction( "FoldLeft" );
2477+
DeclareOperation( "FoldLeftOp", [ IsListOrCollection, IsFunction ] );
2478+
DeclareOperation( "FoldLeftOp", [ IsListOrCollection, IsFunction, IsObject ] );
2479+
2480+
2481+
#############################################################################
2482+
##
2483+
#O FoldLeftX( <gens>, <func>, <init>[, <abortValue>] )
2484+
##
2485+
## <#GAPDoc Label="FoldLeftX">
2486+
## <ManSection>
2487+
## <Func Name="FoldLeftX" Arg='gens, func, init[, abortValue]'/>
2488+
##
2489+
## <Description>
2490+
## TODO
2491+
## </Description>
2492+
## </ManSection>
2493+
## <#/GAPDoc>
2494+
##
2495+
## TODO: document FoldLeftX (based on ListX documentation?)
2496+
##
2497+
DeclareGlobalFunction( "FoldLeftX" );
2498+
2499+
24542500
#############################################################################
24552501
##
24562502
#F Sum( <list>[, <init>] ) . . . . . . . . . . sum of the elements of a list

lib/coll.gi

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1798,6 +1798,102 @@ InstallGlobalFunction( ListX, function ( arg )
17981798
end );
17991799

18001800

1801+
#############################################################################
1802+
##
1803+
#F FoldLeft( <coll>, <func> )
1804+
#F FoldLeft( <coll>, <func>, <init> )
1805+
##
1806+
InstallGlobalFunction( FoldLeft,
1807+
function( arg )
1808+
local tnum, C, func, result, i, l;
1809+
l := Length( arg );
1810+
if l < 2 or l > 3 or not IsFunction(arg[2]) then
1811+
Error( "usage: FoldLeft( <C>, <func>[, <init>] )" );
1812+
fi;
1813+
tnum:= TNUM_OBJ( arg[1] );
1814+
# handle built-in lists directly, to avoid method dispatch overhead
1815+
if FIRST_LIST_TNUM <= tnum and tnum <= LAST_LIST_TNUM then
1816+
C:= arg[1];
1817+
func:= arg[2];
1818+
if l = 2 then
1819+
if IsEmpty( C ) then
1820+
Error("folding an empty collection without initial value is not supported");
1821+
else
1822+
result:= C[1];
1823+
for i in [ 2 .. Length( C ) ] do
1824+
result:= func( result, C[i] );
1825+
od;
1826+
fi;
1827+
else
1828+
result:= arg[3];
1829+
for i in C do
1830+
result:= func( result, i );
1831+
od;
1832+
fi;
1833+
return result;
1834+
else
1835+
return CallFuncList( FoldLeftOp, arg );
1836+
fi;
1837+
end );
1838+
1839+
1840+
#############################################################################
1841+
##
1842+
#M FoldLeftOp( <C>, <func> ) . . . . . . . for a list/collection, and a function
1843+
##
1844+
InstallMethod( FoldLeftOp,
1845+
"for a list/collection, and a function",
1846+
[ IsListOrCollection, IsFunction ],
1847+
function ( C, func )
1848+
local iter, result, x;
1849+
iter := Iterator( C );
1850+
if IsDoneIterator( iter ) then
1851+
Error("folding an empty collection without initial value is not supported");
1852+
fi;
1853+
result := NextIterator( iter );
1854+
for x in iter do
1855+
result := func( result, x );
1856+
od;
1857+
return result;
1858+
end );
1859+
1860+
1861+
#############################################################################
1862+
##
1863+
#M FoldLeftOp( <C>, <func>, <init> ) . for a list/coll., a func., and init. val.
1864+
##
1865+
InstallMethod( FoldLeftOp,
1866+
"for a list/collection, and a function, and an initial value",
1867+
[ IsListOrCollection, IsFunction, IsObject ],
1868+
function ( C, func, init )
1869+
local result, x;
1870+
result := init;
1871+
for x in C do
1872+
result := func( result, x );
1873+
od;
1874+
return result;
1875+
end );
1876+
1877+
1878+
#############################################################################
1879+
##
1880+
#M FoldLeftX(<obj>,...)
1881+
##
1882+
InstallGlobalFunction( FoldLeftX, function ( gens, f, init, extra... )
1883+
local abortValue;
1884+
1885+
if Length(extra) > 0 then
1886+
abortValue := extra[1];
1887+
else
1888+
# assign a globally unique bag: here, we take a new empty
1889+
# string, which is guaranteed to be different from any
1890+
# other string object
1891+
abortValue := "";
1892+
fi;
1893+
return FOLD_LEFT_X(gens, f, init, abortValue);
1894+
end );
1895+
1896+
18011897
#############################################################################
18021898
##
18031899
#M SetX(<obj>,...)

src/listfunc.c

Lines changed: 123 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@
2424
#include "permutat.h"
2525
#include "plist.h"
2626
#include "pperm.h"
27+
#include "precord.h"
28+
#include "records.h"
2729
#include "set.h"
30+
#include "stats.h"
2831
#include "stringobj.h"
2932
#include "sysfiles.h"
3033
#include "trans.h"
@@ -1460,6 +1463,121 @@ static Obj FuncLIST_WITH_IDENTICAL_ENTRIES(Obj self, Obj n, Obj obj)
14601463
return list;
14611464
}
14621465

1466+
static Obj FastCallFuncList(Obj func, Obj list)
1467+
{
1468+
switch (LEN_PLIST(list)) {
1469+
case 0:
1470+
return CALL_0ARGS(func);
1471+
case 1:
1472+
return CALL_1ARGS(func, ELM_PLIST(list, 1));
1473+
case 2:
1474+
return CALL_2ARGS(func, ELM_PLIST(list, 1), ELM_PLIST(list, 2));
1475+
case 3:
1476+
return CALL_3ARGS(func, ELM_PLIST(list, 1), ELM_PLIST(list, 2),
1477+
ELM_PLIST(list, 3));
1478+
case 4:
1479+
return CALL_4ARGS(func, ELM_PLIST(list, 1), ELM_PLIST(list, 2),
1480+
ELM_PLIST(list, 3), ELM_PLIST(list, 4));
1481+
case 5:
1482+
return CALL_5ARGS(func, ELM_PLIST(list, 1), ELM_PLIST(list, 2),
1483+
ELM_PLIST(list, 3), ELM_PLIST(list, 4),
1484+
ELM_PLIST(list, 5));
1485+
case 6:
1486+
return CALL_6ARGS(func, ELM_PLIST(list, 1), ELM_PLIST(list, 2),
1487+
ELM_PLIST(list, 3), ELM_PLIST(list, 4),
1488+
ELM_PLIST(list, 5), ELM_PLIST(list, 6));
1489+
default:
1490+
return CALL_XARGS(func, list);
1491+
}
1492+
}
1493+
1494+
static Obj IsListOrCollection;
1495+
1496+
// TODO: document this a bit
1497+
static int FoldLeftXHelp(Obj gens,
1498+
Obj foldFunc,
1499+
Obj * acc,
1500+
Obj abortValue,
1501+
Obj args,
1502+
int genIndex,
1503+
int valIndex)
1504+
{
1505+
while (genIndex <= LEN_PLIST(gens)) {
1506+
Obj gen = ELM_PLIST(gens, genIndex);
1507+
if (IS_FUNC(gen))
1508+
gen = FastCallFuncList(gen, args);
1509+
if (gen == True)
1510+
genIndex++;
1511+
else if (gen == False)
1512+
return 0;
1513+
else if (IS_LIST(gen)) {
1514+
const int len = LEN_LIST(gen);
1515+
for (int i = 1; i <= len; i++) {
1516+
Obj elm = ELM0_LIST(gen, i);
1517+
if (!elm)
1518+
continue; // skip holes
1519+
AssPlist(args, valIndex, elm);
1520+
if (FoldLeftXHelp(gens, foldFunc, acc, abortValue, args,
1521+
genIndex + 1, valIndex + 1))
1522+
return 1;
1523+
}
1524+
UNB_LIST(args, valIndex);
1525+
return 0;
1526+
}
1527+
else if (CALL_1ARGS(IsListOrCollection, gen) == True) {
1528+
// get the iterator
1529+
Obj iter = CALL_1ARGS(ITERATOR, gen);
1530+
1531+
Obj nfun, dfun;
1532+
if (IS_PREC_OR_COMOBJ(iter) &&
1533+
CALL_1ARGS(STD_ITER, iter) == True) {
1534+
// this can avoid method selection overhead on iterator
1535+
dfun = ElmPRec(iter, RNamName("IsDoneIterator"));
1536+
nfun = ElmPRec(iter, RNamName("NextIterator"));
1537+
}
1538+
else {
1539+
dfun = IS_DONE_ITER;
1540+
nfun = NEXT_ITER;
1541+
}
1542+
1543+
// loop over the iterator
1544+
while (CALL_1ARGS(dfun, iter) == False) {
1545+
1546+
// get the element and assign it to the variable
1547+
Obj elm = CALL_1ARGS(nfun, iter);
1548+
1549+
AssPlist(args, valIndex, elm);
1550+
if (FoldLeftXHelp(gens, foldFunc, acc, abortValue, args,
1551+
genIndex + 1, valIndex + 1))
1552+
return 1;
1553+
}
1554+
UNB_LIST(args, valIndex);
1555+
return 0;
1556+
}
1557+
else {
1558+
ErrorMayQuit("gens[%d] must be a collection, a list, a boolean, "
1559+
"or a function",
1560+
genIndex, 0);
1561+
}
1562+
}
1563+
*acc = CALL_2ARGS(foldFunc, *acc, args);
1564+
return abortValue == *acc;
1565+
}
1566+
1567+
// TODO: document this a bit
1568+
Obj FuncFOLD_LEFT_X(
1569+
Obj self, Obj gens, Obj foldFunc, Obj init, Obj abortValue)
1570+
{
1571+
if (!IS_PLIST(gens))
1572+
return Fail;
1573+
if (!IS_FUNC(foldFunc))
1574+
return Fail;
1575+
1576+
Obj args = NEW_PLIST(T_PLIST, LEN_PLIST(gens));
1577+
FoldLeftXHelp(gens, foldFunc, &init, abortValue, args, 1, 1);
1578+
return init;
1579+
}
1580+
14631581
/****************************************************************************
14641582
**
14651583
*F * * * * * * * * * * * * * initialize module * * * * * * * * * * * * * * *
@@ -1488,7 +1606,7 @@ static StructGVarOper GVarOpers [] = {
14881606
**
14891607
*V GVarFuncs . . . . . . . . . . . . . . . . . . list of functions to export
14901608
*/
1491-
static StructGVarFunc GVarFuncs [] = {
1609+
static StructGVarFunc GVarFuncs[] = {
14921610

14931611
GVAR_FUNC_2ARGS(APPEND_LIST_INTR, list1, list2),
14941612
GVAR_FUNC_2ARGS(POSITION_SORTED_LIST, list, obj),
@@ -1511,6 +1629,9 @@ static StructGVarFunc GVarFuncs [] = {
15111629
GVAR_FUNC(COPY_LIST_ENTRIES, -1, "srclist,srcstart,srcinc,dstlist,dststart,dstinc,number"),
15121630
GVAR_FUNC_1ARGS(STRONGLY_CONNECTED_COMPONENTS_DIGRAPH, digraph),
15131631
GVAR_FUNC_2ARGS(LIST_WITH_IDENTICAL_ENTRIES, n, obj),
1632+
1633+
GVAR_FUNC_4ARGS(FOLD_LEFT_X, gens, foldFunc, init, abortValue),
1634+
15141635
{ 0, 0, 0, 0, 0 }
15151636

15161637
};
@@ -1532,8 +1653,8 @@ static Int InitKernel (
15321653
InitHdlrOpersFromTable( GVarOpers );
15331654
InitHdlrFuncsFromTable( GVarFuncs );
15341655

1656+
ImportFuncFromLibrary("IsListOrCollection", &IsListOrCollection);
15351657

1536-
15371658
return 0;
15381659
}
15391660

src/stats.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ static UInt ExecIfElifElse(Stat stat)
340340
Obj ITERATOR;
341341
Obj IS_DONE_ITER;
342342
Obj NEXT_ITER;
343-
static Obj STD_ITER;
343+
Obj STD_ITER;
344344

345345
static ALWAYS_INLINE UInt ExecForHelper(Stat stat, UInt nr)
346346
{

src/stats.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ void ClearError(void);
134134
extern Obj ITERATOR;
135135
extern Obj IS_DONE_ITER;
136136
extern Obj NEXT_ITER;
137+
extern Obj STD_ITER;
137138

138139

139140
/****************************************************************************

tst/testinstall/coll.tst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,21 @@ gap> List(iter);
315315
gap> List(iter, x -> x+1);
316316
[ 43 ]
317317
318+
#############################################################################
319+
#
320+
# FoldLeft
321+
#
322+
gap> FoldLeft([1..10], \*) = Factorial(10);
323+
true
324+
gap> FoldLeft([1..10], \+) = Sum([1..10]);
325+
true
326+
gap> FoldLeft([1..10], \+, 5) = Sum([1..10], 5);
327+
true
328+
gap> FoldLeft([1..3], \-);
329+
-4
330+
gap> FoldLeft([1..3], \-, 0);
331+
-6
332+
318333
#############################################################################
319334
#
320335
# List

0 commit comments

Comments
 (0)