Skip to content
Snippets Groups Projects
Unverified Commit 486cc3c8 authored by Yorick Peterse's avatar Yorick Peterse
Browse files

Add Iterator.partition and std::pair

The method Iterator.partition can be used to partition an Iterator into
a Pair of Array objects. The return type is the newly introduced
std::pair::Pair, which is a simple binary tuple. We also introduce
std::pair::Triple for ternary pairs. We do not introduce any further
tuple types, as custom objects are better suited for pairs of more than
three values.
parent 075c7af5
No related branches found
No related tags found
No related merge requests found
Loading
Loading
@@ -187,7 +187,7 @@ module Inkoc
 
TypeSystem::Error.new
else
symbol.type
remap_send_return_type(symbol.type, scope)
end
end
 
Loading
Loading
Loading
Loading
@@ -27,6 +27,7 @@ import std::nil
import std::block
import std::string
import std::array
import std::iterator
import std::array::extensions::(self as _)
import std::map::(Map as _Map)
import std::inspect
Loading
Loading
Loading
Loading
@@ -150,6 +150,8 @@
# returns the characters in a `String`.
 
# A generic iterator over a sequence of values of type `T`.
import std::pair::Pair
trait Iterator!(T) {
# Advances the iterator and returns the next value.
#
Loading
Loading
@@ -304,6 +306,34 @@ trait Iterator!(T) {
)
}
 
# Partitions the `Iterator` into a `Pair` of two `Array` objects.
#
# The first value of the `Pair` contains all values for which the supplied
# block returned `True`. The second value contains all values for which the
# block returned `False`.
#
# # Examples
#
# Partitioning an `Iterator`:
#
# let values = Array.new(10, 20, 30, 40, 50)
# let pair = values.iter.partition do (value) { value >= 30 }
#
# pair.first # => Array.new(30, 40, 50)
# pair.second # => Array.new(10, 20)
def partition(block: do (T) -> Boolean) -> Pair!(Array!(T), Array!(T)) {
let true = Array.new
let false = Array.new
each do (value) {
block
.call(value)
.if(true: { true.push(value) }, false: { false.push(value) })
}
Pair.new(true, false)
}
# Transforms the `Iterator` into an `Array`.
#
# This method will advance the iterator to the end.
Loading
Loading
# Types for binary and ternary tuples.
#
# This module only offers types for binary and ternary tuples. If you need a
# combination of more values (e.g. a 4-arity tuple), it's best to define your
# own type instead.
import std::format::(Formatter, Inspect)
import std::hash::*
import std::operators::Equal
# A pair of two values.
object Pair!(A, B) {
# The first value of this `Pair`.
@first: A
# The second value of this `Pair`.
@second: B
def init(first: A, second: B) {
@first = first
@second = second
}
# Returns the first value of this `Pair`.
#
# # Examples
#
# Obtaining the first value of a `Pair`:
#
# import std::pair::Pair
#
# let pair = Pair.new(10, 'foo')
#
# pair.first # => 10
def first -> A {
@first
}
# Returns the second value of this `Pair`.
#
# # Examples
#
# Obtaining the second value of a `Pair`:
#
# import std::pair::Pair
#
# let pair = Pair.new(10, 'foo')
#
# pair.second # => 'foo'
def second -> B {
@second
}
}
impl Inspect for Pair!(A, B) {
def format_for_inspect(formatter: Formatter) where A: Inspect, B: Inspect {
formatter.push('Pair { ')
formatter.descend {
@first.format_for_inspect(formatter)
}
formatter.push(', ')
formatter.descend {
@second.format_for_inspect(formatter)
}
formatter.push(' }')
}
}
impl Equal for Pair!(A, B) {
def ==(other: Self) -> Boolean where A: Equal, B: Equal {
(@first == other.first).and { @second == other.second }
}
}
impl Hash for Pair!(A, B) {
def hash(hasher: Hasher) where A: Hash, B: Hash {
@first.hash(hasher)
@second.hash(hasher)
}
}
# A pair of three values.
object Triple!(A, B, C) {
# The first value of this `Pair`.
@first: A
# The second value of this `Pair`.
@second: B
# The third value of this `Pair`.
@third: C
def init(first: A, second: B, third: C) {
@first = first
@second = second
@third = third
}
# Returns the first value of this `Triple`.
#
# # Examples
#
# Obtaining the first value of a `Triple`:
#
# import std::pair::Triple
#
# let triple = Triple.new(10, 'foo', 20)
#
# triple.first # => 10
def first -> A {
@first
}
# Returns the second value of this `Triple`.
#
# # Examples
#
# Obtaining the second value of a `Triple`:
#
# import std::pair::Triple
#
# let triple = Triple.new(10, 'foo', 20)
#
# triple.second # => 'foo'
def second -> B {
@second
}
# Returns the third value of this `Triple`.
#
# # Examples
#
# Obtaining the second value of a `Triple`:
#
# import std::pair::Triple
#
# let triple = Triple.new(10, 'foo', 20)
#
# triple.third # => 20
def third -> C {
@third
}
}
impl Inspect for Triple!(A, B, C) {
def format_for_inspect(
formatter: Formatter
) where A: Inspect, B: Inspect, C: Inspect {
formatter.push('Triple { ')
formatter.descend {
@first.format_for_inspect(formatter)
}
formatter.push(', ')
formatter.descend {
@second.format_for_inspect(formatter)
}
formatter.push(', ')
formatter.descend {
@third.format_for_inspect(formatter)
}
formatter.push(' }')
}
}
impl Equal for Triple!(A, B, C) {
def ==(other: Self) -> Boolean where A: Equal, B: Equal, C: Equal {
(@first == other.first)
.and { @second == other.second }
.and { @third == other.third }
}
}
impl Hash for Triple!(A, B, C) {
def hash(hasher: Hasher) where A: Hash, B: Hash, C: Hash {
@first.hash(hasher)
@second.hash(hasher)
@third.hash(hasher)
}
}
Loading
Loading
@@ -45,6 +45,7 @@ import test::std::test_mirror
import test::std::test_nil
import test::std::test_object
import test::std::test_os
import test::std::test_pair
import test::std::test_process
import test::std::test_random
import test::std::test_range
Loading
Loading
Loading
Loading
@@ -139,6 +139,24 @@ test.group('std::iterator::Iterator.select') do (g) {
}
}
 
test.group('std::iterator::Iterator.partition') do (g) {
g.test('Partitioning an empty Iterator') {
let iter = EmptyIterator.new
let pair = iter.partition do (value) { value >= 20 }
assert.true(pair.first.empty?)
assert.true(pair.second.empty?)
}
g.test('Partitioning an Iterator with value') {
let iter = SimpleIterator.new
let pair = iter.partition do (value) { value >= 20 }
assert.equal(pair.first, Array.new(20, 30))
assert.equal(pair.second, Array.new(10))
}
}
test.group('std::iterator::Iterator.to_array') do (g) {
g.test('Converting an Iterator to an Array') {
let iter = SimpleIterator.new
Loading
Loading
import std::map::DefaultHasher
import std::pair::*
import std::test
import std::test::assert
test.group('std::pair::Pair.first') do (g) {
g.test('Obtaining the first value of a Pair') {
assert.equal(Pair.new(10, 20).first, 10)
}
}
test.group('std::pair::Pair.second') do (g) {
g.test('Obtaining the second value of a Pair') {
assert.equal(Pair.new(10, 20).second, 20)
}
}
test.group('std::pair::Pair.==') do (g) {
g.test('Comparing two Pair objects for equality') {
assert.not_equal(Pair.new(1, 2), Pair.new(2, 3))
assert.equal(Pair.new(1, 1), Pair.new(1, 1))
}
}
test.group('std::pair::Pair.hash') do (g) {
g.test('Hashing a Pair') {
let hasher1 = DefaultHasher.new(1, 2)
let hasher2 = DefaultHasher.new(1, 2)
let pair = Pair.new(1, 2)
pair.hash(hasher1)
pair.hash(hasher2)
assert.equal(hasher1.to_hash, hasher2.to_hash)
}
}
test.group('std::pair::Pair.inspect') do (g) {
g.test('Inspecting a Pair') {
let pair = Pair.new(10, 20)
assert.equal(pair.inspect, 'Pair { 10, 20 }')
}
}
test.group('std::pair::Triple.first') do (g) {
g.test('Obtaining the first value of a Triple') {
assert.equal(Triple.new(10, 20, 30).first, 10)
}
}
test.group('std::pair::Triple.second') do (g) {
g.test('Obtaining the second value of a Triple') {
assert.equal(Triple.new(10, 20, 30).second, 20)
}
}
test.group('std::pair::Triple.third') do (g) {
g.test('Obtaining the third value of a Triple') {
assert.equal(Triple.new(10, 20, 30).third, 30)
}
}
test.group('std::pair::Triple.==') do (g) {
g.test('Comparing two Triple objects for equality') {
assert.not_equal(Triple.new(1, 2, 3), Triple.new(2, 3, 4))
assert.equal(Triple.new(1, 1, 1), Triple.new(1, 1, 1))
}
}
test.group('std::pair::Triple.hash') do (g) {
g.test('Hashing a Triple') {
let hasher1 = DefaultHasher.new(1, 2)
let hasher2 = DefaultHasher.new(1, 2)
let triple = Triple.new(1, 2, 3)
triple.hash(hasher1)
triple.hash(hasher2)
assert.equal(hasher1.to_hash, hasher2.to_hash)
}
}
test.group('std::pair::Triple.inspect') do (g) {
g.test('Inspecting a Triple') {
let triple = Triple.new(10, 20, 30)
assert.equal(triple.inspect, 'Triple { 10, 20, 30 }')
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment