task 3 - right fold

This commit is contained in:
2025-09-28 14:57:02 +02:00
parent b54016d5df
commit 910e758a75
2 changed files with 133 additions and 0 deletions

View File

@@ -31,3 +31,59 @@ fun {Sum List}
case List of Head|Tail then Head + {Sum Tail} else 0 end
end
% task 3 a)
fun {RightFold List Op U} % U is the 'default' value of the reduction
case List of Head|Tail then {Op Head {RightFold Tail Op U}} else U end
end
% task 3 c)
local RFSum RFLength in
fun {RFSum List}
{RightFold List fun {$ X Y} X + Y end 0}
end
{System.show 'are Sum and RFSum the same?'}
{System.show ({RFSum [1 2 3 4]} == {Sum [1 2 3 4]})}
%
% NOTICE: THIS IS COMMENTED OUT BECAUSE IT DEPENDS ON THE `\insert '...'` STATEMENT.
%
% fun {RFLength List}
% {RightFold List fun {$ X Y} X + 1 end 0}
% end
% {System.show 'are Length and RFLength the same?'}
% {System.show ({RFSum [1 2 3 4]} == {Sum [1 2 3 4]})}
end
% task 3 d) example fold
local NonAssociativeOperator in
fun {NonAssociativeOperator X Y}
{Pow X Y}
end
{System.show 'are FoldL and FoldR the same under a non-associative operator?'}
{System.show {FoldL [1 2 3 4] NonAssociativeOperator 0}
== {FoldR [1 2 3 4] NonAssociativeOperator 0}} % note: FoldR is already provided
end
% task 3 e) Factorial example
local Iota Product Factorial in
{System.show 'Iota 5 and Factorial 5:'}
% same as python's range(1, N+1) -- but in reverse order.
% all natural numbers less than or equal to N.
% name is borrowed from APL.
fun {Iota N}
if N > 0 then N|{Iota N-1} else nil end
end
{System.show {Iota 5}}
% multiplies all elements in a list together
fun {Product List}
{RightFold List fun {$ X Y} X*Y end 1} % U=1
end
% factorial is trivially implemented as a fold
fun {Factorial N}
{Product {Iota N}}
end
{System.show {Factorial 5}}
end

View File

@@ -50,3 +50,80 @@ fun {Sum List}
end
```
## task 3
### a) & b)
the recursive structure of the right fold is the same as that of sum and length. they both simply pop an element from the list, then apply the binary operator on that element and the recursively accumulated value.
folds can be thought of as putting the binary operator infix between each element of the list, then evaluating it. they are also called "reduce" (in languages like APL/BQN/Uiua), because they apply an operation to all the elements of an array such that it *reduces* the rank of the array by one, i.e. making a matrix a vector, or a vector a scalar.
in my mind, the right fold implementation is a single line, because i am quite used to thinking in terms of folds.
```oz
fun {RightFold List Op U} % U is the 'default' value of the reduction
case List of Head|Tail % grab the first element
then {Op Head {RightFold Tail Op U}} % recursively apply operator
else U % use a default value when the list is empty
end
end
```
### c)
the functions can be defined easily with a fold:
```oz
fun {RFSum List}
{RightFold List fun {$ X Y} X + Y end 0}
end
fun {RFLength List}
{RightFold List fun {$ X Y} X + 1 end 0}
end
```
### d)
because addition is both associative and commutative, the elements in the List passed to Sum or Length can be in any order and also summed or counted in any order. thus, using a left-associative fold would yield the same result.
the following code snippet uses exponentiation as an example of a non-commutative and non-associative binary operator that would yield different results for the same list.
here, using a left-associative fold results in 0, because the base is set to 0 (U=0), while the right-associative fold results in 1, since 0 is the last exponent.
```oz
fun {NonAssociativeOperator X Y}
{Pow X Y}
end
% are FoldL and FoldR the same under a non-associative operator?
% note: FoldL is already provided
{System.show {FoldL [1 2 3 4] NonAssociativeOperator 0} % = 0
== {FoldR [1 2 3 4] NonAssociativeOperator 0}} % = 1
% -> false
```
### e)
another example is the factorial function. this illustrates that the identity element of multiplication is 1, and is thus suited as the U-element for a fold under multiplication. setting U=0 would always yield 0 under a multiplication-reduction.
```oz
% same as python's range(1, N+1) -- but in reverse order.
% all natural numbers less than or equal to N.
% name is borrowed from APL.
fun {Iota N}
if N > 0 then N|{Iota N-1} else nil end
end
{System.show {Iota 5}} % -> [5 4 3 2 1]
% multiplies all elements in a list together
fun {Product List}
{RightFold List fun {$ X Y} X*Y end 1} % note: U=1
end
% factorial is trivially implemented as a fold
fun {Factorial N}
{Product {Iota N}}
end
{System.show {Factorial 5}} % -> 5! = 120
```