What is difference between plus and plusElement (minus, minusElement) functions over the (immutable) List in practice?
operator fun <T> Collection<T>.plus(element: T): List<T>
fun <T> Collection<T>.plusElement(element: T): List<T>
Besides plus and minus being operators and therefore simplifiable to + and - respectively, I wanted to share an example, that may make it more clear, why plusElement or minusElement may also make sense to use. Basically that's the case when you do not want the overloaded operator methods to be called (e.g. plus(elements : Iterable<T>)), which may be the case when you are dealing with a list of lists.
Maybe the following samples make that clearer. In the samples all variable assignments show the type they got assigned when calling the respective function and contain the result in the comment at the end of the line. The variable ~ending naming convention is the following:
PlusT show calls to plus(element : T)
PlusIterable show calls to plus(elements : Iterable<T>)
PlusElementT show calls to plusElement(element : T)
Samples:
val someEntry = "some entry"
val listOfSomeEntry = listOf(someEntry)
val los : List<String> = listOf("listOfString")
val lsPlusT : List<String> = los.plus(someEntry) // [listOfString, some entry]
val lsPlusIterable1 : List<String> = los.plus(listOfSomeEntry) // [listOfString, some entry]
val lsPlusIterable2 : List<Any> = los.plus(listOf(listOfSomeEntry)) // [listOfString, [some entry]]
val lsPlusElementT1 : List<String> = los.plusElement(someEntry) // [listOfString, some entry]
val lsPlusElementT2 : List<Any> = los.plusElement(listOfSomeEntry) // [listOfString, [some entry]]
val lol : List<List<String>> = listOf(listOf("listOfList"))
// the following is basically not really correct as we are now dealing with a list of lists of strings, but it shows that llPlusT and llPlusIterable lead to the same (in this case probably wrong) result..
val llPlusT : List<Any> = lol.plus(someEntry) // [[listOfList], some entry]
val llPlusIterable : List<Any> = lol.plus(listOfSomeEntry) // [[listOfList], some entry]
val llPlusIterable2 : List<List<String>> = lol.plus(listOf(listOfSomeEntry)) // [[listOfList], [some entry]]
val llPlusElement1 : List<Any> = lol.plusElement(someEntry) // [[listOfList], some entry]
val llPlusElement2 : List<List<String>> = lol.plusElement(listOfSomeEntry) // [[listOfList], [some entry]]
As you can see when using + the overloaded variant plus(elements : Iterable<T>) might be used, which will probably make sense in most cases, but may not make sense in some others, e.g. (most of the times) when dealing with a list of lists. Instead of forcing the + to add a list of lists by using + listOf(anotherList), you may rather want to use plusElement (plusElement(anotherList)) or if you are sure you want to add only a single element, you may want to omit plus in favor of plusElement (probably a very rare and very special use case... that would be reflected with variant llPlusElement1).
Finally the plusElement or minusElement make it really clear from the naming that what you pass reflects 1 single item of the list, whereas + leaves that basically open... (you should see that however from the context, which with a list of list is probably not that clear anyways ;-)). And a disclaimer at the end: that should not mean that you should use a list of lists, but just in case you find something like that you have plus/minusElement at hand ;-)
The first one is an overloaded operator, as indicated by the operator keyword. It allows you to use the + operator with a List<T>.
The second one is a regular function, called in the normal function call style.
Both return a new List<T> with the element appended.
Transcript from the Kotlin REPL:
>>>val a = listOf(1, 2, 3)
>>>a + 4
[1, 2, 3, 4]
>>> a.plusElement(4)
[1, 2, 3, 4]
For more information about Kotlin operator overloading, see https://kotlinlang.org/docs/reference/operator-overloading.html.
Related
I'm practicing leetcode problems to perfect my kotlin syntax and am wondering why this code doesn't work. My question specifically is why doesn't my courses hashmap populate with this code.
Prerequisites is an array in this form [[0,1][0,3][4,5][6,7]] and if I print my variables for pre and post they print what I expect
But I'm trying to turn courses into an adjacency matrix like this {0: [1,3], 4: [5], 6: [7]}
and instead it just prints an empty set every time
class Solution {
fun canFinish(numCourses: Int, prerequisites: Array<IntArray>): Boolean {
val courses = HashMap<Int, MutableList<Int>>().withDefault{ mutableListOf<Int>() }
for ((pre, post) in prerequisites){
courses[pre]?.add(post)
}
print(courses)
return false
}
}
stdout: {}
[] does not give you the default value
From the docs of withDefault:
This implicit default value is used when the original map doesn't contain a value for the key specified and a value is obtained with Map.getValue function
If you want to get the default value, you need to use getValue instead of the index operator.
Using the index operator, you would just get null and because of the the null-safe operator, the add operation would not even be executed.
If you take a look at the relevant source code, you can see that the funxtionality get is not changed when using .withDefault but only getOrImplicitDefault returns the default value.
Getting the default does not set anything
Furthermore, when accessing courses.getValue(pre) in the loop, the Map will be empty. Because of the withDefault, it will return a MutableList where you can add elements but getting such a list and adding elements to it will not add the list to the Map. Reading and accessing an element does not insert it.
Simple solution
If you want to make sure the element is present in the Map, you can use courses[pre]=course.getValue(pre) before reading courses[pre]?:
class Solution {
fun canFinish(numCourses: Int, prerequisites: Array<IntArray>): Boolean {
val courses = HashMap<Int, MutableList<Int>>().withDefault{ mutableListOf<Int>() }
for ((pre, post) in prerequisites){
courses[pre] = courses.getValue(pre)
courses[pre]?.add(post)
}
print(courses)
return false
}
}
If the entry is set already, it will be set to itself (no change) and if it isn't set, it will be set to the default value (empty list).
dan1st's answer covers it - your default list is just returned, not put and returned, so it's not part of the map - but here's a different take to get that functionality:
val courses = HashMap<Int, MutableList<Int>>().run {
withDefault{ key ->
mutableListOf<Int>().also { put(key, it) }
}
}
So basically using the withDefault wrapper, using run so the map is this in the default value function, so you can add your list to the map before returning it. Then when you call courses.getValue(69) you'll get back a list that's already been inserted into the map
If you like, there's also a function that'll do this grouping for you, groupBy
val nums = arrayOf(
intArrayOf(0,1),
intArrayOf(0,3),
intArrayOf(4,5),
intArrayOf(6,7)
)
val groups = nums.groupBy(keySelector = { it[0] }, valueTransform = { it[1] })
println(groups)
>> {0=[1, 3], 4=[5], 6=[7]}
I used graph dsl to create some stream processing jobs based on some example code I saw. Everything runs great, I am just having trouble understanding the notation: (updated for 2.4)
def elements: Source[Foos] = ...
def logEveryNSink = // a sink that logs
def cleaner: Flow[Foos, Bars, Unit] = ...
def boolChecker(bar: Bar)(implicit ex: ExecutionContext): Future[Boolean] = ...
val mySink = Sink.foreach[Boolean](println(_))
val lastly = Flow[Bars].mapAsync(2)(x => boolChecker(x).toMat(mySink)(Keep.right)
val materialized = RunnableGraph.fromGraph(
GraphDSL.create(lastly) { implicit builder =>
baz => {
import GraphDSL.Implicits._
val broadcast1 = builder.add(Broadcast[Foos](2))
val broadcast2 = builder.add(Broadcast[Bars](2))
elements ~> broadcast1 ~> logEveryNSink(1)
broadcast1 ~> cleaner ~> broadcast2 ~> baz
~> broadcast2 ~> logEveryNSink(1)
ClosedShape
}
}
).run()
I understand the implicit builder that is included, but Im uncertain what the baz represents in { implicit builder => baz => { .... is it just an implicit name for the entire shape?
The GraphDSL.create method is heavily overloaded to take in many variants of amounts of input shapes (including 0). If you pass in no initial shapes, then the signature of the buildBlock function arg (the body where you actually define how the graph is to be built) is as follows:
(Builder[NotUsed]) => S
So this is simply a Function1[Builder[NotUsed], S], that is, a function that takes an instance of a Builder[NotUsed] and returns a Shape instance which is the final graph. The NotUsed here is synonymous with Unit in that you are saying that by not passing in any input shares that you do not care about the materialized value of the output graph being produced.
If you do decide to pass in input shapes, then the signature of that buildBlock function changes a bit to accomadate the input shapes. In your case, you are passing in 1 input shape, so the signature of buildBlock changes to:
(Builder[Mat]) => Graph.Shape => S
Now, this is essentially a Function1[Builder[Mat], Function1[Graph.Shape, S]], or a function that takes a Builder[Mat] (where Mat is the materialized value type of the input shape) and returns a function that takes a Graph.Shape and returns an instance of S (which is a Shape).
Long story short, if you pass in shapes, then you also need to declare them as bound params on the graph building block function but as a second input function (hence the additional =>).
I am trying to swap elements in a list in ML. My swap function returns the error inserting EQUALOP.
fun swap(n:int, i:int, deck:card list) =
local
val card1_removed = nth(deck,i)
val card2_removed = nth(deck,n)
in
val deck = remove(deck,i)
val deck = remove(deck,n)
val deck = insert_at(deck,n,card1_removed)
val deck = insert_at(deck,i,card2_removed)
print_cards(deck);
end;
Any suggestions?
There are a few problems with your code.
First off you cant have a local declaration inside a function definition like that. The body of a function must be an expression, and local ... in .. end is a declaration. In this case you have to use let ... in .. end, which is an expression.
Note that you can't have value declarations in the in ... end part of a let-expression though. Here you will have to move all the value declarations up in the let ... in part.
To be a bit more clear the form of let and local is:
<atexp> ::= let <dec> in <exp_1> ; ... ; <exp_n> end
<dec> ::= local <dec_1> in <dec_2> end
Thus, normally local is used like this
local
fun foo ...
val ....
in
fun swap ...
end
where let is used like this
fun swap ...
let
val ...
in
..
end
How does this work in Scala?
val something = List(1,2,3)
List is abstract, and you can't construct it by invoking new List(), but List(1,2,3) works just fine.
Because it is a call to the apply method of the list companion object. In scala, a method called apply can be called with the method name ommitted (i.e. just with parens). It is by this mechanism that sequence and Map access works
Hence, List(1, 2, 3) is in fact:
List.apply(1, 2, 3)
So this is then a call to the apply method of List's companion object, the implementation of which is:
override def apply[A](xs: A*): List[A] = xs.toList
So you can see that apply is a method which takes a repeating parameter (a sequence), and calls the toList method on this sequence. This toList on Seq is inherited from TraversableOnce:
def toList: List[A] = new ListBuffer[A] ++= seq toList
So you can see that this creates a list via a ListBuffer and the ++= method:
override def ++=(xs: TraversableOnce[A]): this.type =
if (xs eq this) ++= (this take size) else super.++=(xs)
This ultimately gets its implementation of ++= from Growable:
def ++=(xs: TraversableOnce[A]): this.type = { xs.seq foreach += ; this }
Invoking new List() does not work because List is a trait (and a sealed one at that) - you would have had to apply implementations for the abstract methods. The fact that it is sealed means that it can only be implemented by a class in the same source file.
List(1,2,3) is "magic syntax" to call the apply method in the companion object of trait or class List.
I have a List of type [T] and [B] in scala, with an object e of type E.
I want to make a function that accepts those three parameters:
def doSomething(t : List[T], b List[B], e : E) {
... }
However I realise that List is immutable, and anything passed to a function is considered as val (not var). But I need to modify t and b and return the modifications back to the caller of the function. Does anyone have any idea how to do this?
I can't go and change the list to array... Because I've been using it everywhere and the file is so big..
You should modify t and b in a functional way using higher order functions like map, filter,... and put the result of them into new vals (e.g. modifiedT, modifiedB). Then you can use a Tuple2 to return 2 values from the method.
def doSomething(t: List[T], b: List[B], e: E) = {
// somehting you want to do
val modifiedT = t.map(...).filter(...)
val modifiedB = b.map(...).filter(...)
(modifiedT, modifiedB) // returns a Tuple2[List[T], List[B]]
}
In the calling method you can then assign the values this way:
val (t2, b2) = doSomething(t, b, e)
Of course it depends on what you mean with "modify".
If this modification is complicated stuff you should consider using view to make calculation lazy to move the time of calculation to a later point in time.