Skip to content

Commit

Permalink
Merge pull request #86 from MoeOrganization/prakashk/array-methods
Browse files Browse the repository at this point in the history
few more array methods
  • Loading branch information
Stevan Little committed Apr 24, 2013
2 parents dccf0a1 + 644b0e0 commit a3dfb52
Show file tree
Hide file tree
Showing 9 changed files with 324 additions and 3 deletions.
56 changes: 53 additions & 3 deletions src/main/scala/org/moe/runtime/builtins/ArrayClass.scala
Expand Up @@ -313,16 +313,66 @@ object ArrayClass {
)
)

arrayClass.addMethod(
new MoeMethod(
"exists",
new MoeSignature(List(new MoePositionalParameter("$item"))),
env,
(e) => self(e).exists(r, e.get("$item").get)
)
)

arrayClass.addMethod(
new MoeMethod(
"uniq",
new MoeSignature(),
env,
(e) => self(e).uniq(r)
)
)

arrayClass.addMethod(
new MoeMethod(
"zip",
new MoeSignature(List(new MoePositionalParameter("@that"))),
env,
(e) => self(e).zip(r, e.getAs[MoeArrayObject]("@that").get)
)
)

arrayClass.addMethod(
new MoeMethod(
"kv",
new MoeSignature(),
env,
(e) => self(e).kv(r)
)
)

arrayClass.addMethod(
new MoeMethod(
"classify",
new MoeSignature(List(new MoePositionalParameter("&mapper"))),
env,
(e) => self(e).classify(r, e.getAs[MoeCode]("&mapper").get)
)
)

arrayClass.addMethod(
new MoeMethod(
"categorize",
new MoeSignature(List(new MoePositionalParameter("&mapper"))),
env,
(e) => self(e).categorize(r, e.getAs[MoeCode]("&mapper").get)
)
)

/**
* List of Methods to support:
* - exists ($value)
* - delete ($index | @indicies)
* - sort ($sorter)
* - first ($predicate)
* - min ($comparator)
* - max ($comparator)
* - sum
*
* See the following for details:
* - https://metacpan.org/release/autobox-Core
Expand Down
15 changes: 15 additions & 0 deletions src/main/scala/org/moe/runtime/builtins/CorePackage.scala
Expand Up @@ -166,5 +166,20 @@ object CorePackage {
)
)

pkg.addSubroutine(
new MoeSubroutine(
"rand",
new MoeSignature(List(new MoeOptionalParameter("$limit"))),
env,
{ (e) =>
val limit = e.get("$limit") match {
case Some(none: MoeUndefObject) => 1.0
case Some(value: MoeObject) => value.unboxToDouble.get
case _ => 1.0
}
getNum(limit * scala.util.Random.nextDouble)
}
)
)
}
}
53 changes: 53 additions & 0 deletions src/main/scala/org/moe/runtime/nativeobjects/MoeArrayObject.scala
Expand Up @@ -3,6 +3,7 @@ package org.moe.runtime.nativeobjects
import org.moe.runtime._

import scala.collection.mutable.ArrayBuffer
import scala.collection.mutable.HashMap
import scala.util.{Try, Success, Failure}

class MoeArrayObject(
Expand Down Expand Up @@ -134,6 +135,58 @@ class MoeArrayObject(
r.NativeObjects.getArray(result: _*)
}

def exists (r: MoeRuntime, item: MoeObject): MoeBoolObject = r.NativeObjects.getBool(
array.exists( x => x.equal_to(item) )
)

// this should preserve the order in the input list
def uniq (r: MoeRuntime): MoeArrayObject = {
val uniq_set = array.foldLeft (List[MoeObject]()) { (s, x) => if (s.exists(y => y.equal_to(x))) s else (x :: s) }
r.NativeObjects.getArray(uniq_set.reverse: _*)
}

def zip (r: MoeRuntime, that: MoeArrayObject): MoeArrayObject = {
val zipped = for ((x, y) <- unboxToArrayBuffer.get zip that.unboxToArrayBuffer.get)
yield r.NativeObjects.getArray(x, y)
r.NativeObjects.getArray(zipped: _*)
}

def kv (r: MoeRuntime): MoeArrayObject = {
val indexed = for ((k, v) <- List.range(0, array.length) zip unboxToArrayBuffer.get)
yield r.NativeObjects.getArray(r.NativeObjects.getInt(k), v)
r.NativeObjects.getArray(indexed: _*)
}

def append (r: MoeRuntime, item: MoeObject) = {
array += item
this
}

def classify (r: MoeRuntime, mapper: MoeCode): MoeHashObject = {
val classified = array.foldLeft (new HashMap[String, MoeObject]()) {
(hm, i) =>
val key = mapper.execute(new MoeArguments(List(i))).unboxToString.get
hm += ((key, hm.getOrElse(key, r.NativeObjects.getArray()).asInstanceOf[MoeArrayObject].append(r, i)))
}
r.NativeObjects.getHash(classified)
}

def categorize (r: MoeRuntime, mapper: MoeCode): MoeHashObject = {
val categorized = array.foldLeft (new HashMap[String, MoeObject]()) {
(hm, i) =>
val categories = mapper.execute(new MoeArguments(List(i))).unboxToArrayBuffer.get
categories.foreach(
{
c =>
val key = c.unboxToString.get
hm += ((key, hm.getOrElse(key, r.NativeObjects.getArray()).asInstanceOf[MoeArrayObject].append(r, i)))
}
)
hm
}
r.NativeObjects.getHash(categorized)
}

// equality
def equal_to (r: MoeRuntime, that: MoeArrayObject): MoeBoolObject =
r.NativeObjects.getBool(
Expand Down
63 changes: 63 additions & 0 deletions src/test/scala/org/moe/parser/ArrayMethodTestSuite.scala
Expand Up @@ -8,6 +8,7 @@ import org.moe.runtime._
import org.moe.interpreter._
import org.moe.ast._
import org.moe.parser._
import org.moe.runtime.nativeobjects._

class ArrayMethodTestSuite extends FunSuite with BeforeAndAfter with ParserTestUtils with ShouldMatchers {

Expand Down Expand Up @@ -135,4 +136,66 @@ class ArrayMethodTestSuite extends FunSuite with BeforeAndAfter with ParserTestU
val result = interpretCode("""my @a = ["a", "b", "c"]; @a.join""")
result.unboxToString.get should equal ("abc")
}

test("... basic test with array.exists") {
val result = interpretCode("""my @a = [2, 3, 4]; 1..5.map(-> ($x) { @a.exists($x) } )""")
val array = result.unboxToArrayBuffer.get
array.length should equal (5)
array(0).unboxToBoolean.get should equal (false)
array(1).unboxToBoolean.get should equal (true)
array(2).unboxToBoolean.get should equal (true)
array(3).unboxToBoolean.get should equal (true)
array(4).unboxToBoolean.get should equal (false)
}

test("... basic test with array.uniq") {
val result = interpretCode("""my @a = [1, 1, 2, 3, 4, 5, 4]; @a.uniq.join(",")""")
result.unboxToString.get should equal ("1,2,3,4,5")
}

test("... basic test with array.zip") {
val result = interpretCode("""my @a = ["a", "b", "c"]; my @b = 1..3; @a.zip(@b).flatten.join""")
result.unboxToString.get should equal ("a1b2c3")
}

test("... basic test with array.kv") {
val result = interpretCode("""my @a = ["a", "b", "c"]; @a.kv.flatten.join""")
result.unboxToString.get should equal ("0a1b2c")
}

test("... basic test with array.classify") {
val result = interpretCode("""1..5.classify(-> ($x) { $x % 2 ? "odd" : "even" })""")
val map = result.unboxToMap.get

val odds = map("odd").asInstanceOf[MoeArrayObject].unboxToArrayBuffer.get
odds.length should be (3)
odds(0).unboxToInt.get should equal (1)
odds(1).unboxToInt.get should equal (3)
odds(2).unboxToInt.get should equal (5)

val evens = map("even").asInstanceOf[MoeArrayObject].unboxToArrayBuffer.get
evens.length should be (2)
evens(0).unboxToInt.get should equal (2)
evens(1).unboxToInt.get should equal (4)
}

test("... basic test with array.categorize") {
val result = interpretCode("""1..5.categorize(-> ($x) { my @c = [$x % 2 ? "odd" : "even"]; if ($x % 3 == 0) { @c.push('triple') } @c })""")
val map = result.unboxToMap.get

val odds = map("odd").asInstanceOf[MoeArrayObject].unboxToArrayBuffer.get
odds.length should be (3)
odds(0).unboxToInt.get should equal (1)
odds(1).unboxToInt.get should equal (3)
odds(2).unboxToInt.get should equal (5)

val evens = map("even").asInstanceOf[MoeArrayObject].unboxToArrayBuffer.get
evens.length should be (2)
evens(0).unboxToInt.get should equal (2)
evens(1).unboxToInt.get should equal (4)

val triples = map("triple").asInstanceOf[MoeArrayObject].unboxToArrayBuffer.get
triples.length should be (1)
triples(0).unboxToInt.get should equal (3)
}
}
25 changes: 25 additions & 0 deletions src/test/scala/org/moe/parser/BuiltinTestSuite.scala
@@ -0,0 +1,25 @@
package org.moe.parser

import org.scalatest.FunSuite
import org.scalatest.BeforeAndAfter
import org.scalatest.matchers.ShouldMatchers

import org.moe.runtime._
import org.moe.runtime.nativeobjects._
import org.moe.interpreter._
import org.moe.ast._
import org.moe.parser._

class BuiltinTestSuite extends FunSuite with BeforeAndAfter with ParserTestUtils with ShouldMatchers {
test("... rand -- with no argument") {
val result = interpretCode("rand()").unboxToDouble.get
result should be >= (0.0)
result should be < (1.0)
}

test("... rand -- with a limit argument") {
val result = interpretCode("rand(10)").unboxToDouble.get
result should be >= (0.0)
result should be < (10.0)
}
}
6 changes: 6 additions & 0 deletions t/099-problems/14.t
Expand Up @@ -14,5 +14,11 @@ is_deeply(duplicate(["a", "b", "c", "c", "d"]),
["a", "a", "b", "b", "c", "c", "c", "c", "d", "d"],
"... P14");

sub duplicate_by_zip(@list) { @list.zip(@list).flatten }

is_deeply(duplicate_by_zip(["a", "b", "c", "c", "d"]),
["a", "a", "b", "b", "c", "c", "c", "c", "d", "d"],
"... P14 (zip)");

done_testing();

37 changes: 37 additions & 0 deletions t/099-problems/23.t
@@ -0,0 +1,37 @@
# P23 (**) Extract a given number of randomly selected elements from a list.
# Example:

# moe> random_select(3, ['a', 'b', 'c', 'd', 'f', 'g', 'h'])
# ['e', 'd', 'a']

# Note: the selection is done without replacement; there should be no
# duplicates in output

use Test::More;

# from P20
sub remove_at($i, @list) {
my $removed = @list[$i];
my @rest = @list[0 .. ($i-1)];
@rest.push(@list[($i+1) .. (@list.length-1)]);
[@rest.flatten, $removed]
}

sub random_select($n, @list) {
my @l = @list;
1..$n.map(-> {
my @result = remove_at(rand(@l.length).Int, @l);
@l = @result[0];
@result[1]
})
}

my @picks = random_select(3, ['a', 'b', 'c', 'd', 'f', 'g', 'h']);

# can't really test for specific values in the result
is(@picks.length, 3, "... P23");

# check for duplicates
ok(@picks.uniq.length == @picks.length, "... P23 -- no duplicates");

done_testing();
33 changes: 33 additions & 0 deletions t/099-problems/24.t
@@ -0,0 +1,33 @@
# P24 (*) Lotto: Draw N different random numbers from the set 1..M.
# Example:

# moe> lotto(6, 49)
# [23, 1, 17, 33, 21, 37]

use Test::More;

# from P20
sub remove_at($i, @list) {
my $removed = @list[$i];
my @rest = @list[0 .. ($i-1)];
@rest.push(@list[($i+1) .. (@list.length-1)]);
[@rest.flatten, $removed]
}

# from P23
sub random_select($n, @list) {
my @l = @list;
1..$n.map(-> {my @result = remove_at(rand(@l.length).Int, @l); @l = @result[0]; @result[1]})
}

sub lotto($n, $m) { random_select($n, 1..$m) }

my @picks = lotto(6, 49);
say @picks;

is(@picks.length, 6, "... P24 -- number of picks");
ok(@picks.uniq.length == @picks.length, "... P24 -- no duplicates");
0..5.each(-> ($i) { ok(@picks[$i] > 0 && @picks[$i] <= 49, "... P24 -- pick " ~ ~($i + 1)) });

done_testing();

39 changes: 39 additions & 0 deletions t/099-problems/25.t
@@ -0,0 +1,39 @@
# P25 (*) Generate a random permutation of the elements of a list.
# Hint: Use the solution of problem P23.

# Example:

# moe> random_permute(['a', 'b', 'c', 'd', 'e', 'f'])
# ['b', 'a', 'd', 'c', 'e', 'f']

use Test::More;

# from P20
sub remove_at($i, @list) {
my $removed = @list[$i];
my @rest = @list[0 .. ($i-1)];
@rest.push(@list[($i+1) .. (@list.length-1)]);
[@rest.flatten, $removed]
}

sub random_permute(@list) {
my @l = @list;
1..(@l.length).map(-> {
if (@l.length > 1) {
my @result = remove_at(rand(@l.length).Int, @l);
@l = @result[0];
@result[1]
}
else {
@l[0]
}
})
}

my @list = ['a', 'b', 'c', 'd', 'e', 'f'];
my @permuted = random_permute(@list);

is(@permuted.length, @list.length, "... P25");
@permuted.each(-> ($x) { ok(@list.exists($x), "... P25 -- exists " ~ $x.Str) });

done_testing();

0 comments on commit a3dfb52

Please sign in to comment.