diff --git a/program.own b/program.own index 9aac799..859a3c3 100644 --- a/program.own +++ b/program.own @@ -1,5 +1,7 @@ use "math" use "std" +use "functional" + word = 2 + 2 word2 = PI + word str = "a" * 5 + "ba" * 7 + "\n" @@ -117,3 +119,9 @@ thread(::inthread) def inthread() = echo("this is a thread") thread(def (str) { println str }, "this is a thread with arguments") +println "functional" +nums = [1,2,3,4,5,6,7,8,9,10] +nums = filter(nums, def(x) = x % 2 == 0) +squares = map(nums, def(x) = x * x) +foreach(squares, ::echo) +println "Sum: " + reduce(squares, 0, def(x, y) = x + y) \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/ArrayValue.java b/src/com/annimon/ownlang/lib/ArrayValue.java index 427c17d..e34b8ce 100644 --- a/src/com/annimon/ownlang/lib/ArrayValue.java +++ b/src/com/annimon/ownlang/lib/ArrayValue.java @@ -47,6 +47,10 @@ public final class ArrayValue implements Value, Iterable { return Types.ARRAY; } + public int size() { + return elements.length; + } + public Value get(int index) { return elements[index]; } diff --git a/src/com/annimon/ownlang/lib/MapValue.java b/src/com/annimon/ownlang/lib/MapValue.java index 1c77a97..c55bd94 100644 --- a/src/com/annimon/ownlang/lib/MapValue.java +++ b/src/com/annimon/ownlang/lib/MapValue.java @@ -25,6 +25,10 @@ public class MapValue implements Value, Iterable> { public int type() { return Types.MAP; } + + public int size() { + return map.size(); + } public Value get(Value key) { return map.get(key); diff --git a/src/com/annimon/ownlang/lib/modules/functional.java b/src/com/annimon/ownlang/lib/modules/functional.java new file mode 100644 index 0000000..e5bc229 --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functional.java @@ -0,0 +1,19 @@ +package com.annimon.ownlang.lib.modules; + +import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.lib.modules.functions.*; + +/** + * + * @author aNNiMON + */ +public final class functional implements Module { + + @Override + public void init() { + Functions.set("foreach", new functional_foreach()); + Functions.set("map", new functional_map()); + Functions.set("reduce", new functional_reduce()); + Functions.set("filter", new functional_filter()); + } +} diff --git a/src/com/annimon/ownlang/lib/modules/functions/functional_filter.java b/src/com/annimon/ownlang/lib/modules/functions/functional_filter.java new file mode 100644 index 0000000..c1f7ed9 --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/functional_filter.java @@ -0,0 +1,52 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.lib.*; +import java.util.ArrayList; +import java.util.List; + +import java.util.Map; + +public final class functional_filter implements Function { + + @Override + public Value execute(Value... args) { + if (args.length < 2) throw new RuntimeException("At least two args expected"); + + if (args[1].type() != Types.FUNCTION) { + throw new RuntimeException("Function expected in second arg"); + } + final Value container = args[0]; + final Function consumer = ((FunctionValue) args[1]).getValue(); + if (container.type() == Types.ARRAY) { + return filterArray((ArrayValue) container, consumer); + } + + if (container.type() == Types.MAP) { + return filterMap((MapValue) container, consumer); + } + + throw new RuntimeException("Invalid first argument. Array or map exprected"); + } + + private Value filterArray(ArrayValue array, Function predicate) { + final int size = array.size(); + final List values = new ArrayList(size); + for (Value value : array) { + if (predicate.execute(value) != NumberValue.ZERO) { + values.add(value); + } + } + final int newSize = values.size(); + return new ArrayValue(values.toArray(new Value[newSize])); + } + + private Value filterMap(MapValue map, Function predicate) { + final MapValue result = new MapValue(map.size()); + for (Map.Entry element : map) { + if (predicate.execute(element.getKey(), element.getValue()) != NumberValue.ZERO) { + result.set(element.getKey(), element.getValue()); + } + } + return result; + } +} \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/functions/functional_foreach.java b/src/com/annimon/ownlang/lib/modules/functions/functional_foreach.java new file mode 100644 index 0000000..bf9bf33 --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/functional_foreach.java @@ -0,0 +1,34 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.lib.*; + +import java.util.Map; + +public final class functional_foreach implements Function { + + @Override + public Value execute(Value... args) { + if (args.length != 2) throw new RuntimeException("Two args expected"); + + if (args[1].type() != Types.FUNCTION) { + throw new RuntimeException("Function expected in second arg"); + } + final Value container = args[0]; + final Function consumer = ((FunctionValue) args[1]).getValue(); + if (container.type() == Types.ARRAY) { + final ArrayValue array = (ArrayValue) container; + for (Value element : array) { + consumer.execute(element); + } + return NumberValue.ZERO; + } + if (container.type() == Types.MAP) { + final MapValue map = (MapValue) container; + for (Map.Entry element : map) { + consumer.execute(element.getKey(), element.getValue()); + } + return NumberValue.ZERO; + } + throw new RuntimeException("Invalid first argument. Array or map exprected"); + } +} \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/functions/functional_map.java b/src/com/annimon/ownlang/lib/modules/functions/functional_map.java new file mode 100644 index 0000000..980106c --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/functional_map.java @@ -0,0 +1,55 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.lib.*; + +import java.util.Map; + +public final class functional_map implements Function { + + @Override + public Value execute(Value... args) { + if (args.length < 2) throw new RuntimeException("At least two args expected"); + + final Value container = args[0]; + if (container.type() == Types.ARRAY) { + if (args[1].type() != Types.FUNCTION) { + throw new RuntimeException("Function expected in second arg"); + } + final Function mapper = ((FunctionValue) args[1]).getValue(); + return mapArray((ArrayValue) container, mapper); + } + + if (container.type() == Types.MAP) { + if (args[1].type() != Types.FUNCTION) { + throw new RuntimeException("Function expected in second arg"); + } + if (args[2].type() != Types.FUNCTION) { + throw new RuntimeException("Function expected in third arg"); + } + final Function keyMapper = ((FunctionValue) args[1]).getValue(); + final Function valueMapper = ((FunctionValue) args[2]).getValue(); + return mapMap((MapValue) container, keyMapper, valueMapper); + } + + throw new RuntimeException("Invalid first argument. Array or map exprected"); + } + + private Value mapArray(ArrayValue array, Function mapper) { + final int size = array.size(); + final ArrayValue result = new ArrayValue(size); + for (int i = 0; i < size; i++) { + result.set(i, mapper.execute(array.get(i))); + } + return result; + } + + private Value mapMap(MapValue map, Function keyMapper, Function valueMapper) { + final MapValue result = new MapValue(map.size()); + for (Map.Entry element : map) { + final Value newKey = keyMapper.execute(element.getKey()); + final Value newValue = valueMapper.execute(element.getValue()); + result.set(newKey, newValue); + } + return result; + } +} \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/functions/functional_reduce.java b/src/com/annimon/ownlang/lib/modules/functions/functional_reduce.java new file mode 100644 index 0000000..7a886eb --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/functional_reduce.java @@ -0,0 +1,37 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.lib.*; + +import java.util.Map; + +public final class functional_reduce implements Function { + + @Override + public Value execute(Value... args) { + if (args.length != 3) throw new RuntimeException("Three args expected"); + + if (args[2].type() != Types.FUNCTION) { + throw new RuntimeException("Function expected in third arg"); + } + final Value container = args[0]; + final Value identity = args[1]; + final Function accumulator = ((FunctionValue) args[2]).getValue(); + if (container.type() == Types.ARRAY) { + Value result = identity; + final ArrayValue array = (ArrayValue) container; + for (Value element : array) { + result = accumulator.execute(result, element); + } + return result; + } + if (container.type() == Types.MAP) { + Value result = identity; + final MapValue map = (MapValue) container; + for (Map.Entry element : map) { + result = accumulator.execute(result, element.getKey(), element.getValue()); + } + return result; + } + throw new RuntimeException("Invalid first argument. Array or map exprected"); + } +} \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/std.java b/src/com/annimon/ownlang/lib/modules/std.java index dfa4409..3919698 100644 --- a/src/com/annimon/ownlang/lib/modules/std.java +++ b/src/com/annimon/ownlang/lib/modules/std.java @@ -12,7 +12,6 @@ public final class std implements Module { @Override public void init() { Functions.set("echo", new std_echo()); - Functions.set("foreach", new std_foreach()); Functions.set("newarray", new std_newarray()); Functions.set("rand", new std_rand()); Functions.set("sleep", new std_sleep());