How to understand the fun buildString(builderAction: (StringBuilder) -> Unit) : String in Kotlin? - android

The following code is from https://github.com/gbaldeck/learning-kotlin/blob/master/src/main/kotlin/org/learning/DSLconstruction.kt
I find it hard to understand.
1: The fun buildString only accept one lambda parameter in Section A, why are there two parameters passed in Section B?
2: What is full code of Section B?
Such as
val s = buildString { aa : StringBuild -> aa.append("Hello.") } // I don't know whether it's right?
3: What is this it in Section B? Does this it represent StringBuild ?
Section A
fun buildString(builderAction: (StringBuilder) -> Unit ) : String {
val sb = StringBuilder()
builderAction(sb)
return sb.toString()
}
Section B
val s = buildString {
it.append("Hello, ")
it.append("World!")
}
logError(s) //The result is Hello, World!

1: The fun buildString only accept one lambda parameter in Section A, why are there two parameters passed in Section B?
There is only 1 parameter passed to that function: specifically, the builderAction of type (StringBuilder) -> Unit.
So
val s = buildString {
it.append("Hello, ")
it.append("World!")
}
is equivalent to
val s: String = buildString(builderAction = { stringBuilder: StringBuilder ->
stringBuilder.append("Hello, ")
stringBuilder.append("World!")
// return Unit
})
Meaning it is actually the unnamed single argument of (StringBuilder) -> Unit, so it's a StringBuilder.

Yes, it is the StringBuilder. It is named it by default. You can specify the name if you want to.
buildString {
it.append("...")
}
is the same as
buildString { it ->
it.append("...")
}
or even
buildString { myNewString ->
myNewString.append("...")
}

There is only one parameter being passed in section B, namely, this parameter:
{
it.append("Hello, ")
it.append("World!")
}
That is one lambda expression, not two. The lambda expression has 2 lines, but it's still one lambda.
If you want to expand the call to buildString,
val builder = StringBuilder()
builder.append("Hello, ")
builder.append("World!")
val s = builder.toString()
Yes, the it refers to the StringBuilder sb in buildString. When the function type has only one parameter, you can refer to the single parameter with it in the lambda expression without giving it a name.

Related

Whats the Kotlin Syntax called where you have two names after var or val: var (name1, name2) =

I downloaded a project and I'm not really sure what exactly the following line does:
val (episode, setEpisode) = remember { mutableStateOf<EpisodeDetail?>(null) }
The only thing I don't get is why there are two names after the "val" word.
I tried to google for it but I really don't know the name of the syntax.
It's called a Destructuring Declaration
https://kotlinlang.org/docs/destructuring-declarations.html
You may have seen something similar in JavaScript when you have an object, and you can extract the keys to variables with the following
const { key1, key2 } = { key1:"first", key2:"second", ignored:"third" };
console.log(key1, key2) // first second
If you have a data class Kotlin will create the component<N> functions for you.
class MyClass (val myStr: String, val myInt: Int, val myBool: Boolean) {
operator fun component1(): String = myStr
operator fun component2(): Int = myInt
operator fun component3(): Boolean = myBool
}
fun main() {
val x = MyClass("Hello", 5, false)
val (y, _, z) = x // use _ to ignore values you don't need
println(y) // Hello
println(z) // false
}
Unlike Javascript which uses the key names, Kotlin data classes use field ordering (by defining your own component<N> functions you could swap the order of destructuring).

What is a full code of {"456"} for transform: (String) -> String in Kotlin?

transform is lambda fun with the definition (String) -> String
I was told that {"456"} is a valid value of transform, what is a full code of {"456"} for transform: (String) -> String ?
Is Code A correct?
Code A
val aa="123".myAdd{a:String -> "456"}
Code B
val aa="123".myAdd{"456"}
fun String.myAdd(transform: (String) -> String ) = this + transform(this)
Using your example code, let me try to explain what it's doing.
val result = "123".add { it -> "456" }
fun String.add(transform: (String) -> String ) {
this + transform.invoke(this)
}
First, you're creating a String "123", and you're calling a lambda on that. Now, within your lambda, it will be this "123" String.
Normally, you would modify this input in some way, such as a List.Sort(), but you're actually throwing away that input and just returning "456". The last line of your lambda is the return.
For example, we could take the "123" input, and increment each number. This would take your String, convert it to a List of chars, then increment each char, and then join them back together.
val result = "123".add { it -> it.map { it.inc() }.joinToString(separator = "") }
The lambda is taking a String, "123", and applying your transformation and returning a new String. ((String) -> String) I believe that should take "123" and return "234", which will be appended to "123". "123234".
Does that help explain how the lambda (String) -> String works?

How to write an extension fun to append a string in Kotlin?

I'm writing a practice code about extension fun.
I plan to add a extension fun myAdd for String which accept a lambda fun and append the result of the lambda to the String.
For example:
var cc="abc".myAdd{"de"} //I hope to get "abcde", I don't know whether the lambda {"de"} is correct.
But the Code A is wrong, how can I fix it ?
Code A
fun String.myAdd(predicate: (String) -> String): String {
val sb = StringBuilder(this)
sb.append(predicate)
return sb.toString()
}
If you are feeling lucky:
infix fun String.myAdd(transform: (String) -> String) = this + transform(this)
so you can call:
"abc" myAdd { "de" }
Or skip the infix:
fun String.myAdd(transform: (String) -> String) = this + transform(this)
and call it by:
"abc".myAdd { "de" }
which both will result in:
"abcde"
Note that both solutions allow the current string to be passed to the given transformation function before being added. If you didn't want that in the first place but rather wanted a supplier, then you may want to use the following instead:
fun String.myAdd(supplier: () -> String) = this + supplier()
still with the same results
If I understand correctly that you wanted to append the result of the lambda to the original String, you should be doing this:
fun String.myAdd(predicate: (String) -> String): String {
val sb = StringBuilder(this)
sb.append(predicate(this))
return sb.toString()
}
Also, predicate is a very confusing name, it should probably be something else, transform, for example.
fun String.add(append: () -> String) = this + append()
Usage:
val str = "hello".add {
"world"
}
The simplest version of your extension function can be:
fun String.myAdd(append: (String) -> String): String {
return this + append(this)
}
To invoke your function, you should write:
println("Hello, world!".myAdd {
"XD"
})
And it will print Hello, world!XD
Your code doesn't work, because you do not pass to sb.append String, but object of type String -> String. If you want invoke this function, just write (argument) after it (or use method invoke(args)), and then, your append (according to my code above) will be invoked and simply returns String.
Actually, see also that as an argument of your high order function (in my case called append) you pass this object (in my case string Hello).
println("Hello".myAdd {
it.reversed()
}
And it will print HelloolleH.
You don't nee to pass a function, you can just pass a simple String:
fun String.myAdd(predicate: String): String {
val sb = StringBuilder(this)
sb.append(predicate)
return sb.toString()
}
However, I don't think it makes sense to create a Stringbuilder here.
But if it's just a tutorial then that's fine.
Update 1
And if you need the lambda it would be somethink like:
fun String.myAdd(predicate: () -> String): String {
val sb = StringBuilder(this)
sb.append(predicate.invoke())
return sb.toString()
}
Update 2
I wish to write a lambda which accept a string parameter and result the same string as result. How can I write the lambda?
fun main(args: Array<String>) {
val cc = myAdd({ it + "de" })
System.out.println(cc)
}
fun myAdd(predicate: (String) -> String): String {
return predicate.invoke("abc")
}

Why can I invoke a fun without passing parameter name in Kotlin?

There are 4 parameters with default value in function joinToString, in my mind, I should pass parameter value by order when I omit parameter name.
So I think the Code println(letters.joinToString( transform={ it.toLowerCase() } ) ) is right.
But in fact the Code println(letters.joinToString { it.toLowerCase() } ) is right too, why?
fun <T> Collection<T>.joinToString(
separator: String = ", ",
prefix: String = "",
postfix: String = "",
transform: (T) -> String = { it.toString() }
): String {
val result = StringBuilder(prefix)
for ((index, element) in this.withIndex()) {
if (index > 0) result.append(separator)
result.append(transform(element))
}
result.append(postfix)
return result.toString()
}
fun main(args: Array<String>) {
val letters = listOf("Alpha", "Beta")
println(letters.joinToString { it.toLowerCase() } ) //It's Ok
println(letters.joinToString( transform={ it.toLowerCase() } ) ) //It's OK
}
Because you're using a different syntax.
If the last param of a method is a method reference then you can omit the parenthesis and just pass in the function with the { brackets.
it in this case becomes T that you were passing into the function
println(letters.joinToString { it.toLowerCase() } )
Below is what you thought you were entering. This wouldn't compile and would require the named argument or for the params to be in the right order. You would also have to change the syntax from using it to using the regular functional syntax
println(letters.joinToString(it.toLowerCase()))
In addition to #Dan's answer, you don't need to provide a named argument, but if you do so then you're forced to use the named argument for all the following arguments (from the documentation: "all the positional arguments should be placed before the first named one"). In your case the only named argument you're providing is the last one, and all other arguments have default values so you're not forced to provide them, as long as the default value is fine for you.

Kotlin function that receives two numbers and returns sum and multiply of them

I am newbie in Kotlin lambda expressions, I try to build a function to have two arguments and returns a function with to parameters:
val sumAndMultiply:(Int,Int) -> (Int,Int) -> Unit = { a,b -> (a+b,a*b)}
But it's not compiling.
Generally how can I return a headless function in Kotlin?
please help me creating a function that has two arguments and returns a function with two arguments.
This is a function that takes two arguments and returns a function with two arguments.
fun binaryFunReturningBinaryFun(a: Int, b: Int): (Int, Int) -> Int =
{ x, y -> (a + x) * (b + y) }
Kotlin has no support for tuples. You would have to use Pair:
val sumAndMultiply: ((Int,Int) -> Pair<Int,Int>) = { a,b -> Pair(a+b,a*b) }
Another solution would be to create data class, for example:
data class SumAndMultiplication(val sum: Int, val multiplication: Int)
val sumAndMultiply2: ((Int,Int) -> SumAndMultiplication) = { a,b -> SumAndMultiplication(a+b,a*b) }
I have done same thing using below code and it worked.
var rtn:(Int,Int) -> Unit = {x,y -> println("${x} ${y}")}
val sumAndMultiply: (Int,Int) -> Any = { a,b -> rtn(a+b,a*b)}
sumAndMultiply(1,3)
Explanation
You want to return a function from your lambda function so, first of all you have to create the function which you want to return. So, i have created rtn function for this. I have created rtn also as a lambda function. You can create as you want.
Then i have changed return type of sumAndMultiply lambda function and returned function rtn which we have created.

Categories

Resources