I want to infinitely repeat T elements in a Sequence<T>. This can't be done using kotlin.collections.asSequence. For example:
val intArray = intArrayOf(1, 2, 3)
val finiteIntSequence = intArray.asSequence()
val many = 10
finiteIntSequence.take(many).forEach(::print)
// 123
This is not what I want. I expected some kind of kotlin.collections.repeat function to exist, but there isn't, so I implemented one myself (e.g. for this IntArray):
var i = 0
val infiniteIntSequence = generateSequence { intArray[i++ % intArray.size] }
infiniteIntSequence.take(many).forEach(::print)
// 1231231231
This is quite imperative, so I feel there must be a more functional and less verbose way to do this. If it exists, what is/are Kotlin's standard way(s) to repeat collections / arrays a(n) (in)finite amount of times?
Update: coroutines are no longer experimental as of Kotlin 1.3! Use them as much as you like :)
If you allow the use of coroutines you can do this in a pretty clean way using sequence:
an infinite amount of times
fun <T> Sequence<T>.repeat() = sequence { while (true) yieldAll(this#repeat) }
Note the use of a qualified this expression this#repeat - simply using this would refer to the lambda's receiver, a SequenceScope.
then you can do
val intArray = intArrayOf(1, 2, 3)
val finiteIntSequence = intArray.asSequence()
val infiniteIntSequence = finiteIntSequence.repeat()
println(infiniteIntSequence.take(10).toList())
// ^ [1, 2, 3, 1, 2, 3, 1, 2, 3, 1]
a finite amount of times
fun <T> Sequence<T>.repeat(n: Int) = sequence { repeat(n) { yieldAll(this#repeat) } }
To avoid using the experimental coroutines, use:
generateSequence { setOf("foo", 'b', 'a', 'r') }
.flatten() // Put the Iterables' contents into one Sequence
.take(5) // Take 5 elements
.joinToString(", ")
// Result: "foo, b, a, r, foo"
or alternatively, if you want to repeat the entire collection a number of times, just take before flattening:
generateSequence { setOf("foo", 'b', 'a', 'r') }
.take(5) // Take the entire Iterable 5 times
.flatten() // Put the Iterables' contents into one Sequence
.joinToString(", ")
// Result: "foo, b, a, r, foo, b, a, r, foo, b, a, r, foo, b, a, r, foo, b, a, r"
For the original question's IntArray, the array first must be converted to an Iterable<Int> (otherwise flatten() isn't available):
val intArray = intArrayOf(1, 2, 3)
generateSequence { intArray.asIterable() }
.flatten()
.take(10)
.joinToString(", ")
// Result: "1, 2, 3, 1, 2, 3, 1, 2, 3, 1"
Furthermore, other types of Array, e.g. ByteArray or LongArray, as well as Map are not Iterable, but they all implement the asIterable() method like IntArray in the example above.
I think this is pretty clear:
generateSequence(0) { (it + 1) % intArray.size }
.map { intArray[it] }
.forEach { println(it) }
A generic solution would be to reuse the proposal from this answer with extension functions:
fun <T> Array<T>.asRepeatedSequence() =
generateSequence(0) {
(it + 1) % this.size
}.map(::get)
fun <T> List<T>.asRepeatedSequence() =
generateSequence(0) {
(it + 1) % this.size
}.map(::get)
Called like this:
intArray.asRepeatedSequence().forEach(::println)
I'm unsure if this is due to API changes in Kotlin, but it's possible to do the following:
fun <T> Sequence<T>.repeatForever() =
generateSequence(this) { it }.flatten()
Live example: https://pl.kotl.in/W-h1dnCFx
If you happen to have Guava on your classpath, you can do the following:
val intArray = intArrayOf(1, 2, 3)
val cyclingSequence = Iterators.cycle(intArray.asList()).asSequence()
// prints 1,2,3,1,2,3,1,2,3,1
println(cyclingSequence.take(10).joinToString(","))
Related
How can we deconstruct a Pair(SetOf(item),Double)to all possible combination of Pair(Item, Double)?
Hello !
I am trying to learn kotlin and I am struggling with the use of the functional programming part. Especially with the lambda functions and their use on collections.
fun testTeam(): Set<TeamStats>{
val team1: TeamStats=(TeamStats(team(1), setOf(player(1)),3))
val team2: TeamStats=(TeamStats(team(1),players(1..2),5))
val bunchOfTeams: Set<TeamStats> = setOf(team1,team2)
return (bunchOfTeams)
}
fun main (args: Array<String>){
val newTeam: Set<TeamStats> = testTeam()
val decomposition = newTeam.map { Pair(it.Striker,it.Goals) }
println("Stats: $decomposition")
}
I got that result:
/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk....
Stats: [([Striker(name=Player 1)], 3), ([Striker(name=Player 1), Striker(name=Player 2)], 5)]
And I would like to get the following:
Stats: [([Striker(name=Player 1)], 3), ([Striker(name=Player 1),5) ,([Striker(name=Player 2)], 5)]
Maybe there are a better way to obtain that information, please free to suggest !
Thanks again.
From what I understand from your code, it should work using flatMap, e.g.:
newTeam.asSequence().flatMap { stats ->
stats.Striker.asSequence().map {
it to stats.Goals // just a convenience extension function to simplify writing Pair(it, stats.Goals)
}
}
Assuming the following data classes and convenience functions:
data class TeamStats(val team : Team, val Striker: Set<Striker>, val Goals : Int?)
data class Team(val i : Int)
data class Striker(val name : String)
fun player(i : Int) = Striker("Player $i")
fun team(i : Int) = Team(i)
fun players(i : IntRange) = i.asSequence().map(::player).toSet()
Alternatively to the above (and with the shown TeamStats-class), you can also use destructuring as follows:
newTeam.asSequence().flatMap { (_, strikers, goals) ->
strikers.asSequence().map {
it to goals
}
}
which both will give the following result:
Stats: [(Striker(name=Player 1), 3), (Striker(name=Player 1), 5), (Striker(name=Player 2), 5)]
Or if you don't mind that intermediate lists are created you may also just use the above without asSequence(), e.g.:
newTeam.flatMap { (_, strikers, goals) ->
strikers.map {
it to goals
}
}
You need to map the inner Set<T> using a function F: (T) -> Pair<T, Double>.
Example
fun main(args: Array<String>) {
val (set, value) = Pair(setOf(1, 2, 3, 4, 5), 1.0)
val result = set.map { it to value }.toSet()
check(result == setOf(1 to 1.0, 2 to 1.0, 3 to 1.0, 4 to 1.0, 5 to 1.0))
println(result)
}
Output
[(1, 1.0), (2, 1.0), (3, 1.0), (4, 1.0), (5, 1.0)]
I would like to count objects passing from observable. I know there is a count operator but that can't be used for infinite streams because it waits for completition.
What I want is something like Value -> operator -> Pair(Int, Value). I know there could be a problem with int (or long) overflow and that is maybe a reason nothing like this exists but I still have feeling I've seen something like this before. One can implement this with scan operator but I thought there is a simpler way.
Output would be like:
Observable.just(Event1, Event2, Event3) -> (1, Event1), (2, Event2), (3, Event3)
You can solve your problem using the RxJava Scan method:
Observable.just("a", "b", "c")
.scan(new Pair<>(0, ""), (pair, s) -> new Pair<>(pair.first + 1, s))
.skip(1)
Pair<>(0, "") is your seed value
Lambda (pair, s) -> new Pair<>(pair.first + 1, s) takes the seed value and value emitted by original observable and then produces the next Pair value to be emitted and fed back into the lambda
Skip(1) is needed to avoid emitting the seed value
Count means a state change. So you can use a "stateful" map instead of an anonymous class.
ex:
class Mapper<T, R>(val mapper: (T) -> R) : Function<T, Pair<Int, R>> {
private var counter = 0
override fun apply(t: T): Pair<Int, R> {
return Pair(counter++, mapper(t))
//or ++counter if you want start from 1 instead of zero
}
}
//usage
val mapper = Mapper<String, String> { it.toUpperCase() }
Observable.just("a", "b", "c")
.map(mapper)
.subscribe {
Log.d("test logger", it.toString())
}
I have a list of numbers. I want to have the following operation: 1*2 + 3*4. However, the reduce operation works only with one element at a time. And in my case I need to work with two elements at a time. Is it possible to do this with reduce, or with any other method?
package com.zetcode
fun main(args: Array<String>) {
val nums = listOf(1, 2, 3, 4)
val res = nums.reduce { total, next -> total * next }
println(res)
}
You need list.chunked(2) to turn your list into a list of two-member lists and then fold instead of reduce to produce the result (because the type of the result is no longer the same as the type of the list items):
val nums = listOf(1, 2, 3, 4)
val res = nums
.chunked(2)
.fold(0) { total, next -> total + next[0] * next[1] }
println(res)
It's mostly stylistic choice, but I prefer to avoid heavyweight fold functions and instead break down the computation into smaller steps, like this:
val res = nums
.chunked(2)
.map { it.reduce(Int::times) }
.sum()
If you're after performance, having less steps is better due to less intermediate lists created. On the other hand, if you're after performance, you should use lazy sequences to begin with, and then the price of each step is much lower.
My teacher just asked this question in the exam and I have no idea where to go on.
More details, the prototype of function is given as:
stack<int> Fibonacci_sequence(int n); //fibonacci numbers count up to n
The point is this function is recursive and it should return a stack data type. In my opinion I don't think this is a possible thing to do, but my teacher asked it!!
P.s: sorry, my language is C++
function stack<int> Fibonacci_sequence(int n) {
if n == 0 {
var a stack<int>;
a.push(0);
return a
} else if n == 1 {
var a stack<int>;
a.push(0);
a.push(1);
return a
} else
var temp int;
var seq int;
seq = Fibonacci_sequence(n-1);
temp = seq.pop;
seq.push(temp);
seq.push(temp);
//above: the top element of the stack must be duplicated because it
//is popped off in the process of calculating the sum.
seq.push(seq.pop()+Fibonacci_sequence(n-2).pop());
return seq
}
}
Above is a function that does just that, written in pseudo code because you did not specify a language. Hopefully this helps, it was fun to come up with! Thanks for the interesting question.
Since you didn't specify a language, and you specified it's an exam, here it is in Ruby. Ruby provides stack operations for arrays, but I'm only using push and pop operations in the following so you should be able to easily translate it to the language of your choice.
def fib(n) # no explicit return type, since everything's an object in Ruby
fail "negative argument not allowed" if n < 0
if n > 1
stack = fib(n - 1)
# grab the last two values...
f_n_1 = stack.pop
f_n_2 = stack.pop
# ...and use them to calculate the next.
# The value of this expression is the resulting stack, return it
return stack.push(f_n_2).push(f_n_1).push(f_n_1 + f_n_2)
elsif n == 1
return fib(0).push(1)
else
return [].push(0)
end
end
p fib(10) # => [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
You may have to translate this to the language of your exam, but that's appropriate.
Here is my C++ code based on #Elliot pseudo, and it got errors, I specified these errors in the code. And I just figure out that pop() doesn't return a value, I'm gonna fix this.
stack<int> Fibonacci_sequence(int n)
{
if (n == 0) {
stack<int> a;
a.push(0);
return a;
}
else if (n == 1) {
stack<int> a;
a.push(0);
a.push(1);
return a;
}
else
{
int temp;
temp = Fibonacci_sequence(n - 1).pop(); //error C2440: '=': cannot convert from 'void' to 'int'
Fibonacci_sequence(n - 1).push(temp);
Fibonacci_sequence(n - 1).push(temp);
//above: the top element of the stack must be duplicated because it
//is popped off in the process of calculating the sum.
return Fibonacci_sequence(n - 1).push(Fibonacci_sequence(n - 1).pop() + Fibonacci_sequence(n - 2).pop());//error C2186: '+': illegal operand of type 'void'
}
}
Is there a standard library function or built-in construct to concatenate two sequences in JavaFX?
Here a Sequences.concatenate() function is mentioned, but it is nowhere to be seen in the official API.
Of course one could iterate over each sequence, inserting the values into a new sequence e.g:
function concatenate(seqA: Object[], seqB: Object[]) : Object[] {
for(b in seqB) insert b into seqA;
seqA;
}
..but surely something as basic as concatenation is already defined for us somewhere..
It is very simple, since there cannot be sequence in sequence (it all gets flattened), you can do it like this:
var a = [1, 2];
var b = [3, 4];
// just insert one into another
insert b into a;
// a == [1, 2, 3, 4];
// or create a new seq
a = [b, a];
// a == [3, 4, 1, 2];
Hope that helps.