diff --git a/kiss/src/kiss/Kiss.hx b/kiss/src/kiss/Kiss.hx index 5977941e..ac5712dc 100644 --- a/kiss/src/kiss/Kiss.hx +++ b/kiss/src/kiss/Kiss.hx @@ -56,11 +56,9 @@ class Kiss { "print" => Symbol("Prelude.print"), "sort" => Symbol("Prelude.sort"), "groups" => Symbol("Prelude.groups"), - "concat" => Symbol("Prelude.concat"), "pairs" => Symbol("Prelude.pairs"), // TODO test pairs "reversed" => Symbol("Prelude.reversed"), // TODO test reversed "memoize" => Symbol("Prelude.memoize"), // TODO test memoize - "joinPath" => Symbol("Prelude.joinPath"), "symbolName" => Symbol("Prelude.symbolName"), "symbolNameValue" => Symbol("Prelude.symbolNameValue"), "symbol" => Symbol("Prelude.symbol"), @@ -70,11 +68,12 @@ class Kiss { "flatten" => Symbol("Lambda.flatten"), "has" => Symbol("Lambda.has"), "count" => Symbol("Lambda.count"), + // These work with (apply) because they are added as "opAliases" in Macros.kiss: "min" => Symbol("Prelude.min"), - "max" => Symbol("Prelude.max") + "max" => Symbol("Prelude.max"), ], - // These ones won't conflict with variables and might commonly be used with (apply) identAliases: [ + // These ones won't conflict with variables and might commonly be used with (apply) "+" => Symbol("Prelude.add"), "-" => Symbol("Prelude.subtract"), "*" => Symbol("Prelude.multiply"), @@ -85,7 +84,13 @@ class Kiss { ">=" => Symbol("Prelude.greaterEqual"), "<" => Symbol("Prelude.lessThan"), "<=" => Symbol("Prelude.lesserEqual"), - "=" => Symbol("Prelude.areEqual") + "=" => Symbol("Prelude.areEqual"), + // These ones *probably* won't conflict with variables and might commonly be used with (apply) because they are variadic + "concat" => Symbol("Prelude.concat"), + "zipKeep" => Symbol("Prelude.zipKeep"), + "zipDrop" => Symbol("Prelude.zipDrop"), + "zipThrow" => Symbol("Prelude.zipThrow"), + "joinPath" => Symbol("Prelude.joinPath"), ], fields: [], loadingDirectory: "", diff --git a/kiss/src/kiss/Macros.hx b/kiss/src/kiss/Macros.hx index 52acec48..eb203a6a 100644 --- a/kiss/src/kiss/Macros.hx +++ b/kiss/src/kiss/Macros.hx @@ -110,12 +110,6 @@ class Macros { b.callSymbol("Prelude.range", [min, max, step]); }; - macros["zip"] = (wholeExp:ReaderExp, exps:kiss.List, k) -> { - wholeExp.checkNumArgs(3, null, '(zip [listA] [listB] [moreLists...] [extraHandling])'); - var b = wholeExp.expBuilder(); - return b.callSymbol("Prelude.zip", [b.list(exps.slice(0, -1)), exps[-1]]); - }; - // Most conditional compilation macros are based on this macro: macros["#if"] = (wholeExp:ReaderExp, exps:Array, k) -> { wholeExp.checkNumArgs(2, 3, '(#if [cond] [then] [?else])'); diff --git a/kiss/src/kiss/Prelude.hx b/kiss/src/kiss/Prelude.hx index 1f4af8e9..e48f89df 100644 --- a/kiss/src/kiss/Prelude.hx +++ b/kiss/src/kiss/Prelude.hx @@ -181,47 +181,51 @@ class Prelude { public static var concat:Function = Reflect.makeVarArgs(_concat); - public static function zip(arrays:Array>, extraHandling = Drop):kiss.List> { - var lengthsAreEqual = true; - var lengths = [for (arr in arrays) arr.length]; - for (idx in 1...lengths.length) { - if (lengths[idx] != lengths[idx - 1]) { - lengthsAreEqual = false; - break; - } - } - var max = Math.floor(if (!lengthsAreEqual) { - switch (extraHandling) { - case Throw: - throw 'zip was given lists of mis-matched size: $arrays'; - case Keep: - Prelude._max(lengths); - case Drop: - Prelude._min(lengths); - } - } else { - lengths[0]; - }); + static function _zip(iterables:Array, extraHandling:ExtraElementHandling):Array> { + var lists = []; + var iterators = [for (iterable in iterables) iterable.iterator()]; - return [ - for (idx in 0...max) { - var zipped:Array = []; + while (true) { + var zipped:Array = []; - for (arr in arrays) { - zipped.push( - if (idx < arr.length) { - arr[idx]; - } else { - null; - } - ); + var someNonNull = false; + for (it in iterators) { + switch (extraHandling) { + case Keep: + zipped.push( + if (it.hasNext()) { + someNonNull = true; + it.next(); + } else { + null; + }); + default: + if (it.hasNext()) + zipped.push(it.next()); } - - zipped; } - ]; + + switch (extraHandling) { + case _ if (zipped.length == 0): + break; + case Keep if (!someNonNull): + break; + case Drop if (zipped.length != iterators.length): + break; + case Throw if (zipped.length != iterators.length): + throw 'zip${extraHandling} was given iterables of mis-matched size: $iterables'; + default: + } + + lists.push(zipped); + } + return lists; } + public static var zipKeep:Function = Reflect.makeVarArgs(_zip.bind(_, Keep)); + public static var zipDrop:Function = Reflect.makeVarArgs(_zip.bind(_, Drop)); + public static var zipThrow:Function = Reflect.makeVarArgs(_zip.bind(_, Throw)); + public static function pairs(l:kiss.List, loopAround = false):kiss.List> { var l1 = l.slice(0, l.length - 1); var l2 = l.slice(1, l.length); @@ -229,7 +233,7 @@ class Prelude { l1.push(l[-1]); l2.unshift(l[0]); } - return zip([l1, l2]); + return zipThrow(l1, l2); } public static function reversed(l:kiss.List):kiss.List { diff --git a/kiss/src/test/cases/BasicTestCase.kiss b/kiss/src/test/cases/BasicTestCase.kiss index 562c0fbe..95b64800 100644 --- a/kiss/src/test/cases/BasicTestCase.kiss +++ b/kiss/src/test/cases/BasicTestCase.kiss @@ -119,14 +119,16 @@ (catch [error] (Assert.pass)))) (defun _testZip [] - (Assert.equals (.toString [[1 2] [3 4]]) (.toString (zip [1 3] [2 4] Throw))) - (Assert.equals (.toString [[1 2] [3 null]]) (.toString (zip [1 3] [2] Keep))) - (Assert.equals (.toString [[1 2] [null 4]]) (.toString (zip [1 null] [2 4] Keep))) - (try (begin (zip [1 3] [2] Throw) (Assert.fail)) + (Assert.equals (.toString [[1 2] [3 4]]) (.toString (zipThrow [1 3] [2 4]))) + (Assert.equals (.toString [[1 2] [3 null]]) (.toString (zipKeep [1 3] [2]))) + (Assert.equals (.toString [[1 2] [null 4]]) (.toString (zipKeep [1 null] [2 4]))) + (try (begin (zipThrow [1 3] [2]) (Assert.fail)) (catch [error] (Assert.pass))) - (try (begin (zip [1] [2 4] Throw) (Assert.fail)) + (try (begin (zipThrow [1] [2 4]) (Assert.fail)) (catch [error] (Assert.pass))) - (Assert.equals (.toString [[1 2 3] [2 null 3]]) (.toString (zip [1 2] [2] [3 3] Keep)))) + (Assert.equals (.toString [[1 2 3] [2 null 3]]) (.toString (zipKeep [1 2] [2] [3 3]))) + (Assert.equals (.toString [[1 2]]) (.toString (zipDrop [1 2 3 4] [2]))) + (Assert.equals (.toString [[1 2] [3 4]]) (.toString (apply zipThrow [[1 3] [2 4]])))) (defun _testLet [] (let [a 5 diff --git a/projects/asciilib2/src/asciilib/Surface.kiss b/projects/asciilib2/src/asciilib/Surface.kiss index b2dafddc..6083c78c 100644 --- a/projects/asciilib2/src/asciilib/Surface.kiss +++ b/projects/asciilib2/src/asciilib/Surface.kiss @@ -35,9 +35,9 @@ // TODO rectangle type // TODO optional source rectangle argument (defmethod blitSurface [:Surface surface x y] - (doFor [srcX destX] (zip (collect (range 0 surface.width)) (collect (range x (+ x surface.width))) Drop) + (doFor [srcX destX] (zipDrop (range surface.width) (range x (+ x surface.width))) (when (< -1 destX width) - (doFor [srcY destY] (zip (collect (range 0 surface.height)) (collect (range y (+ y surface.height))) Drop) + (doFor [srcY destY] (zipDrop (range 0 surface.height) (range y (+ y surface.height))) (when (< -1 destY height) (when (surface.isCellOpaque srcX srcY) (setBackgroundColor destX destY (surface.getBackgroundColor srcX srcY))