From 2bb5e455178166b18c6c61812baee2b51ea08ed7 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Thu, 5 Oct 2023 18:23:20 +0300 Subject: [PATCH] [functional] Move indexed variant of Stream.forEach to Stream,forEachIndexed --- .../modules/functional/StreamValue.java | 1 + .../functional/functional_forEach.java | 22 +++++-------- .../functional/functional_forEachIndexed.java | 31 +++++++++++++++++++ .../resources/modules/functional/foreach.own | 10 +----- .../resources/modules/functional/stream.own | 22 +++++++++++++ 5 files changed, 62 insertions(+), 24 deletions(-) create mode 100644 modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_forEachIndexed.java diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/StreamValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/StreamValue.java index e4ce962..e246303 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/StreamValue.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/StreamValue.java @@ -30,6 +30,7 @@ class StreamValue extends MapValue { set("reduce", wrapTerminal(new functional_reduce())); set("forEach", wrapTerminal(new functional_forEach())); + set("forEachIndexed", wrapTerminal(new functional_forEachIndexed())); set("toArray", args -> container); set("joining", container::joinToString); set("count", args -> NumberValue.of(container.size())); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_forEach.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_forEach.java index ff4175b..a9b9b7a 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_forEach.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_forEach.java @@ -15,17 +15,16 @@ public final class functional_forEach implements Function { } static Value forEach(Value container, Function consumer) { - final int argsCount = consumer.getArgsCount(); return switch (container.type()) { - case Types.STRING -> forEachString((StringValue) container, argsCount, consumer); - case Types.ARRAY -> forEachArray((ArrayValue) container, argsCount, consumer); + case Types.STRING -> forEachString((StringValue) container, consumer); + case Types.ARRAY -> forEachArray((ArrayValue) container, consumer); case Types.MAP -> forEachMap((MapValue) container, consumer); default -> throw new TypeException("Cannot iterate " + Types.typeToString(container.type())); }; } - static StringValue forEachString(StringValue string, int argsCount, Function consumer) { - if (argsCount == 2) { + static StringValue forEachString(StringValue string, Function consumer) { + if (consumer.getArgsCount() == 2) { for (char ch : string.asString().toCharArray()) { consumer.execute(new StringValue(String.valueOf(ch)), NumberValue.of(ch)); } @@ -37,16 +36,9 @@ public final class functional_forEach implements Function { return string; } - static ArrayValue forEachArray(ArrayValue array, int argsCount, Function consumer) { - if (argsCount == 2) { - int index = 0; - for (Value element : array) { - consumer.execute(element, NumberValue.of(index++)); - } - } else { - for (Value element : array) { - consumer.execute(element); - } + static ArrayValue forEachArray(ArrayValue array, Function consumer) { + for (Value element : array) { + consumer.execute(element); } return array; } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_forEachIndexed.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_forEachIndexed.java new file mode 100644 index 0000000..4ab3ee7 --- /dev/null +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_forEachIndexed.java @@ -0,0 +1,31 @@ +package com.annimon.ownlang.modules.functional; + +import com.annimon.ownlang.exceptions.TypeException; +import com.annimon.ownlang.lib.*; + +public final class functional_forEachIndexed implements Function { + + @Override + public Value execute(Value[] args) { + Arguments.check(2, args.length); + final Value container = args[0]; + final Function consumer = ValueUtils.consumeFunction(args[1], 1); + return forEachIndexed(container, consumer); + } + + static Value forEachIndexed(Value container, Function consumer) { + if (container.type() == Types.ARRAY) { + return forEachIndexedArray((ArrayValue) container, consumer); + } + // Only used in Streams -> no Map implementation + throw new TypeException("Cannot iterate " + Types.typeToString(container.type())); + } + + static ArrayValue forEachIndexedArray(ArrayValue array, Function consumer) { + int index = 0; + for (Value element : array) { + consumer.execute(element, NumberValue.of(index++)); + } + return array; + } +} diff --git a/ownlang-parser/src/test/resources/modules/functional/foreach.own b/ownlang-parser/src/test/resources/modules/functional/foreach.own index ff0a438..24dc567 100644 --- a/ownlang-parser/src/test/resources/modules/functional/foreach.own +++ b/ownlang-parser/src/test/resources/modules/functional/foreach.own @@ -1,6 +1,6 @@ use std, functional -def testArrayForeach1Arg() { +def testArrayForeachArg() { sum = 0 foreach([1, 2, 3], def(v) { sum += v @@ -8,14 +8,6 @@ def testArrayForeach1Arg() { assertEquals(6, sum) } -def testArrayForeach2Args() { - sum = 0 - foreach([1, 2, 3], def(v, index) { - sum += v * index - }) - assertEquals(1 * 0 + 2 * 1 + 3 * 2, sum) -} - def testStringForeach1Arg() { sum = 0 foreach("abcd", def(s) { diff --git a/ownlang-parser/src/test/resources/modules/functional/stream.own b/ownlang-parser/src/test/resources/modules/functional/stream.own index aba004c..bfef4c1 100644 --- a/ownlang-parser/src/test/resources/modules/functional/stream.own +++ b/ownlang-parser/src/test/resources/modules/functional/stream.own @@ -79,6 +79,28 @@ def testSorted() { assertEquals([-2,3,4,-5,6,6,-8], stream(data).sorted(def(a,b) = abs(a) - abs(b)).toArray()) } +def testForEachArrayIndexed() { + data = [1, 2, 3] + sum = 0 + stream(data) + .forEachIndexed(def(v, index) { + sum += (v * index) + }) + assertEquals(1 * 0 + 2 * 1 + 3 * 2, sum) +} + +def testForEachMapIndexed() { + data = {"a": "1", "b": 2} + result = "" + stream(data) + .sorted() + .forEachIndexed(def(entry, index) { + extract(key, value) = entry + result += "" + key + value + index + }) + assertEquals("a10b21", result) +} + def reverse(container) { size = length(container) result = newarray(size)