Skip to content

Commit

Permalink
reduce
Browse files Browse the repository at this point in the history
  • Loading branch information
molarmanful committed Nov 29, 2022
1 parent 6ce01e8 commit 86ec870
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 43 deletions.
63 changes: 42 additions & 21 deletions sclin/src/Lib.scala
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,15 @@ extension (env: ENV)
z.vec1(f => x.rfoldLeft(y)((a, b) => env.evalA1(Vector(a, b), f)))
)

def reduce: ENV = env.mod2((x, y) =>
y.vec1(f =>
x.reduceLeftM(
(a, b) => env.evalA1(Vector(b._1, a, b._2), f),
(a, b) => env.evalA1(Vector(a, b), f)
)
)
)

def scan: ENV = env.mod3((x, y, z) =>
z.vec1(f =>
x.scanLeftM(y)(
Expand Down Expand Up @@ -1338,7 +1347,7 @@ extension (env: ENV)
*/
case "I" => trunc
/*
@s (a >NUM)' -> (1 | 0)'
@s (a >NUM)' -> TF'
Whether `a` is an integer.
*/
case "I?" => isInt
Expand Down Expand Up @@ -2138,9 +2147,9 @@ extension (env: ENV)
/*
@s a f' -> _'
#{Q}s `f` on each element of `a`.
If `a` is `MAP`, then the signature of `f` is `k v -> _ |`,
If `a` is `MAP`, then the signature of `f` is `k v -> _`,
where `k=>v` is the key-value pair.
Otherwise, the signature of `f` is `x -> _ |`,
Otherwise, the signature of `f` is `x -> _`,
where `x` is the element.
```sclin
[1 2 3 4] 1.+ map
Expand All @@ -2159,7 +2168,7 @@ extension (env: ENV)
*/
case "tap" => tapMap
/*
@s a b (f: x y -> _ |)' -> _'
@s a b (f: x y -> _)' -> _'
#{Q}s `f` over each element-wise pair of `a` and `b`.
Iterables of differing length truncate to the shorter length when zipped.
```sclin
Expand All @@ -2174,7 +2183,7 @@ extension (env: ENV)
*/
case "zip" => zip
/*
@s a b c d (f: x y -> _ |)' -> _'
@s a b c d (f: x y -> _)' -> _'
#{zip} but instead of truncating,
uses `c` and `d` as fill elements for `a` and `b` respectively.
```sclin
Expand All @@ -2189,7 +2198,7 @@ extension (env: ENV)
*/
case "zip~" => zip$
/*
@s a b (f: x y -> _ |)' -> _'
@s a b (f: x y -> _)' -> _'
#{Q}s `f` over each table-wise pair of `a` and `b`.
```sclin
[1 2 3 4] [2 3 4 5] \++ tbl
Expand All @@ -2215,9 +2224,9 @@ extension (env: ENV)
/*
@s a b f' -> _'
#{Q}s `f` to combine each accumulator and element starting from initial accumulator `b`.
If `a` is `MAP`, then the signature of `f` is `k x v -> _ |`,
If `a` is `MAP`, then the signature of `f` is `k x v -> _`,
where `k=>v` is the key-value pair and `x` is the accumulator.
Otherwise, the signature of `f` is `x y -> _ |`,
Otherwise, the signature of `f` is `x y -> _`,
where `x` is the accumulator and `y` is the value.
```sclin
[1 2 3 4] 0 \+ fold
Expand All @@ -2239,15 +2248,17 @@ extension (env: ENV)
*/
case "rfold" => rfold
/*
@s a -> NUM'
Sum of `a`. Equivalent to `0 \+ rfold`.
*/
case "+/" => env.push(NUM(0)).push(CMD("+")).rfold
/*
@s a -> NUM'
Product of `a`. Equivalent to `1 \* rfold`.
@s a f' -> _'
#{fold} without initial accumulator, instead using the first element of `a`.
If `a` is empty, then an error is thrown.
```sclin
[1 2 3 4] \+ fold~
```
```sclin
[1 5 10 4 3] \| fold~
```
*/
case "*/" => env.push(NUM(1)).push(CMD("*")).rfold
case "fold~" => reduce
/*
@s a b f' -> _'
#{fold} with intermediate values.
Expand All @@ -2256,6 +2267,16 @@ extension (env: ENV)
```
*/
case "scan" => scan
/*
@s a -> NUM'
Sum of `a`. Equivalent to `0 \+ rfold`.
*/
case "+/" => env.push(NUM(0)).push(CMD("+")).rfold
/*
@s a -> NUM'
Product of `a`. Equivalent to `1 \* rfold`.
*/
case "*/" => env.push(NUM(1)).push(CMD("*")).rfold
// TODO: this doc sucks
/*
@s a f' -> _'
Expand All @@ -2271,9 +2292,9 @@ extension (env: ENV)
/*
@s a f' -> _'
Keeps elements of `a` that satisfy predicate `f`.
If `a` is `MAP`, then the signature of `f` is `k v -> >TF |`,
If `a` is `MAP`, then the signature of `f` is `k v -> >TF`,
where `k=>v` is the key-value pair.
Otherwise, the signature of `f` is `x -> >TF |`,
Otherwise, the signature of `f` is `x -> >TF`,
where `x` is the element.
```sclin
[5 1 2 4 3] 2.> fltr
Expand Down Expand Up @@ -2360,9 +2381,9 @@ extension (env: ENV)
/*
@s a f' -> _'
Sorts elements of `a` with comparator `f`.
If `a` is `MAP`, then the signature of `f` is `j k v w -> >TF |`,
where `j=>v` and `k=>w` are key-value pairs to compare.
Otherwise, the signature of `f` is `x y -> >TF |`,
If `a` is `MAP`, then the signature of `f` is `ARR[k v] ARR[j w] -> >TF`,
where `k=>v` and `j=>w` are key-value pairs to compare.
Otherwise, the signature of `f` is `x y -> >TF`,
where `x` and `y` are elements to compare.
```sclin
[1 5 2 3 4] \< sort~
Expand Down
57 changes: 35 additions & 22 deletions sclin/src/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ enum ANY:
case x: TRY => x.toTry.map(f).toTRY
case _ => toARR.map(f)
def mapM(f: (ANY, ANY) => (ANY, ANY), g: ANY => ANY): ANY = this match
case MAP(x) => x.map { case (a, b) => f(a, b) }.toMAP
case MAP(x) => x.map(f.tupled).toMAP
case _ => map(g)

def flatMap(f: ANY => ANY): ANY = this match
Expand Down Expand Up @@ -380,17 +380,17 @@ enum ANY:
def flat: ANY = flatMap(x => x)

def zip(t: ANY, f: (ANY, ANY) => ANY): ANY = (this, t) match
case (SEQ(x), _) => x.zip(t.toSEQ.x).map { case (x, y) => f(x, y) }.toSEQ
case (_, _: SEQ) => t.zip(this, (x, y) => f(y, x))
case (SEQ(x), _) => x.zip(t.toSEQ.x).map(f.tupled).toSEQ
case (_, _: SEQ) => t.zip(this, (a, b) => f(b, a))
case _ => toARR.x.lazyZip(t.toARR.x).map(f).toARR

def zipAll(t: ANY, d1: ANY, d2: ANY, f: (ANY, ANY) => ANY): ANY =
(this, t) match
case (SEQ(x), _) =>
x.zipAll(t.toSEQ.x, d1, d2).map { case (x, y) => f(x, y) }.toSEQ
x.zipAll(t.toSEQ.x, d1, d2).map(f.tupled).toSEQ
case (_, _: SEQ) => t.zipAll(this, d1, d2, (x, y) => f(y, x)).toSEQ
case _ =>
toARR.x.zipAll(t.toARR.x, d1, d2).map { case (x, y) => f(x, y) }.toARR
toARR.x.zipAll(t.toARR.x, d1, d2).map(f.tupled).toARR

def table(t: ANY, f: (ANY, ANY) => ANY): ANY = map(x => t.map(y => f(x, y)))

Expand All @@ -416,6 +416,24 @@ enum ANY:
)
case x => f(a, x)

def reduceLeft(f: (ANY, ANY) => ANY): ANY = try
this match
case SEQ(x) => x.reduceLeft(f)
case ARR(x) => x.reduceLeft(f)
case _ => toARR.reduceLeft(f)
catch
case e: java.lang.UnsupportedOperationException =>
throw LinEx("ITR", s"unable to reduce empty $getType")
case e => throw e
def reduceLeftM(f: (ANY, (ANY, ANY)) => ANY, g: (ANY, ANY) => ANY): ANY =
this match
case _: MAP =>
toARR.reduceLeft {
case (a, ARR(k +: v +: _)) => f(a, (k, v))
case _ => ???
}
case _ => reduceLeft(g)

def scanLeft(a: ANY)(f: (ANY, ANY) => ANY): ANY = this match
case SEQ(x) => x.scanLeft(a)(f).toSEQ
case ARR(x) => x.scanLeft(a)(f).toARR
Expand All @@ -433,7 +451,7 @@ enum ANY:
case x: TRY => x.toTry.filter(f).toTRY
case _ => toARR.filter(f)
def filterM(f: (ANY, ANY) => Boolean, g: ANY => Boolean): ANY = this match
case MAP(x) => x.filter { case (a, b) => f(a, b) }.toMAP
case MAP(x) => x.filter(f.tupled).toMAP
case _ => filter(g)

def any(f: ANY => Boolean): Boolean = this match
Expand All @@ -442,7 +460,7 @@ enum ANY:
case FN(p, x) => x.exists(f)
case _ => toARR.any(f)
def anyM(f: (ANY, ANY) => Boolean, g: ANY => Boolean): Boolean = this match
case MAP(x) => x.exists { case (a, b) => f(a, b) }
case MAP(x) => x.exists(f.tupled)
case _ => any(g)

def all(f: ANY => Boolean): Boolean = this match
Expand All @@ -451,7 +469,7 @@ enum ANY:
case FN(p, x) => x.forall(f)
case _ => toARR.all(f)
def allM(f: (ANY, ANY) => Boolean, g: ANY => Boolean): Boolean = this match
case MAP(x) => x.forall { case (a, b) => f(a, b) }
case MAP(x) => x.forall(f.tupled)
case _ => all(g)

def takeWhile(f: ANY => Boolean): ANY = this match
Expand All @@ -460,7 +478,7 @@ enum ANY:
case FN(p, x) => x.takeWhile(f).pFN(p)
case _ => toARR.takeWhile(f)
def takeWhileM(f: (ANY, ANY) => Boolean, g: ANY => Boolean): ANY = this match
case MAP(x) => x.takeWhile { case (a, b) => f(a, b) }.toMAP
case MAP(x) => x.takeWhile(f.tupled).toMAP
case _ => takeWhile(g)

def dropWhile(f: ANY => Boolean): ANY = this match
Expand All @@ -469,7 +487,7 @@ enum ANY:
case FN(p, x) => x.dropWhile(f).pFN(p)
case _ => toARR.dropWhile(f)
def dropWhileM(f: (ANY, ANY) => Boolean, g: ANY => Boolean): ANY = this match
case MAP(x) => x.dropWhile { case (a, b) => f(a, b) }.toMAP
case MAP(x) => x.dropWhile(f.tupled).toMAP
case _ => dropWhile(g)

def find(f: ANY => Boolean): Option[ANY] = this match
Expand All @@ -481,7 +499,7 @@ enum ANY:
f: (ANY, ANY) => Boolean,
g: ANY => Boolean
): Option[ANY | (ANY, ANY)] = this match
case MAP(x) => x.find { case (a, b) => f(a, b) }
case MAP(x) => x.find(f.tupled)
case _ => find(g)

def findIndex(f: ANY => Boolean): Int = this match
Expand All @@ -497,7 +515,7 @@ enum ANY:
case _ => toARR.uniqBy(f)
def uniqByM(f: (ANY, ANY) => ANY, g: ANY => ANY): ANY = this match
case MAP(x) =>
x.toSeq.distinctBy { case (a, b) => f(a, b) }.to(VectorMap).toMAP
x.toSeq.distinctBy(f.tupled).to(VectorMap).toMAP
case _ => uniqBy(g)

def sortBy(f: ANY => ANY): ANY = this match
Expand All @@ -507,7 +525,7 @@ enum ANY:
case _ => toARR.sortBy(f)
def sortByM(f: (ANY, ANY) => ANY, g: ANY => ANY): ANY = this match
case MAP(x) =>
x.toSeq.sortBy { case (a, b) => f(a, b) }(OrdANY).to(VectorMap).toMAP
x.toSeq.sortBy(f.tupled)(OrdANY).to(VectorMap).toMAP
case _ => sortBy(g)

def sortWith(f: (ANY, ANY) => Boolean): ANY = this match
Expand All @@ -530,9 +548,7 @@ enum ANY:
def partitionM(f: (ANY, ANY) => Boolean, g: ANY => Boolean): (ANY, ANY) =
this match
case MAP(x) =>
x.partition { case (a, b) => f(a, b) }.pipe { case (a, b) =>
(a.toMAP, b.toMAP)
}
x.partition(f.tupled).pipe { case (a, b) => (a.toMAP, b.toMAP) }
case _ => partition(g)

def groupBy(f: ANY => ANY): Map[ANY, ANY] = this match
Expand All @@ -542,7 +558,7 @@ enum ANY:
case _ => toARR.groupBy(f)
def groupByM(f: (ANY, ANY) => ANY, g: ANY => ANY): Map[ANY, ANY] = this match
case MAP(x) =>
x.groupBy { case (a, b) => f(a, b) }.view.mapValues(_.toMAP).toMap
x.groupBy(f.tupled).view.mapValues(_.toMAP).toMap
case _ => groupBy(g)

def span(f: ANY => Boolean): (ANY, ANY) = this match
Expand All @@ -552,11 +568,8 @@ enum ANY:
case _ => toARR.span(f)
def spanM(f: (ANY, ANY) => Boolean, g: ANY => Boolean): (ANY, ANY) =
this match
case MAP(x) =>
x.span { case (a, b) => f(a, b) }.pipe { case (a, b) =>
(a.toMAP, b.toMAP)
}
case _ => span(g)
case MAP(x) => x.span(f.tupled).pipe { case (a, b) => (a.toMAP, b.toMAP) }
case _ => span(g)

def packBy(f: (ANY, ANY) => Boolean): ANY = this match
case SEQ(x) => x.packBy(f).map(_.toSEQ).toSEQ
Expand Down

0 comments on commit 86ec870

Please sign in to comment.