I'm trying to figure out how to create a function that takes a List of Strings and returns a new list that only contains the longest string in the list.
I'm having a hard time figuring out several aspects of this problem including how to keep track of the maximum and change it when there is a new longest string. Do I need to use an Effect / Task?
Example: a function that takes ["one", "two", "three"] and returns ["three"] since it is the longest string.
If you just want a function that gives you the longest string of a list:
List.sortBy String.length >> List.reverse >> List.take 1
You could fold your list, keeping track of the longest string along the way:
import String exposing (length)
findLongest = List.foldr (\x y -> if length x > length y then x else y) ""
This has a performance advantage over List.sortBy, since foldr is taking a single swipe at the list, while sorting has to sort all the items.
Good news: You don't need to use an Effect or Task.
A longest string function can be defined as follows:
import String exposing (length)
longerString : String -> String -> String
longerString s1 s2 = if length s2 > length s1 then s2 else s1
longestString : List String -> String
longestString = List.foldr longerString ""
Then you can use it like:
longestString ["one", "two", "three"] -- returns "three"
If you want the result as a list with one element, that is also possible.
[ longestString [ "one", "two", "three" ] ] -- returns [ "three" ]
Related
So basically the title I want to change the word letters to another word but it says I have to specify the parameter
fun main() {
val dictionary = mapOf( "c" to "b", "a" to "d" , "r" to "e"//here is the letters i want to change
)
val letters = "car"//the word i want to change
for (me in letters){
println(dictionary[me])
}
}
i want for "car" to be "bde"
Notice that your map has strings as keys, because you used string literals in these places:
val dictionary = mapOf( "c" to "b", "a" to "d" , "r" to "e")
*** *** ***
However, when you access the map, you are accessing it using me, which is a Char you got from the string letters. This causes the error.
I would suggest that you change the map to use Char keys instead. Change the string literals to character literals:
val dictionary = mapOf( 'c' to "b", 'a' to "d" , 'r' to "e")
*** *** ***
Notice that the letters are now surrounded with single quotes.
Though it is not required to solve this particular problem, you can also change the map's values to character literals too.
After that, you should use print instead of println to print the map values out, so that they are all printed on the same line:
for (me in letters){
print(dictionary[me] ?: me.toString())
}
Note that I added the ?: me.toString() part so that if no replacement was found in the map, the original letter would be printed.
If you want a single string as the result, rather than having it printed out,
val result = letters.map { dictionary[it] ?: it.toString() }.joinToString("")
mood = "leet"
modifier = { message ->
val regex = """(L|e|t)""".toRegex()
//Clueless about what to do after this
}
THIS IS WHAT I CAME UP WITH SO FAR, THE QUESTION IN THE BOOK BIG NERD RANCH KOTLIN EDITION 2 SAYS "leet (or 1337): The narrator will speak in leetspeak, replacing letters with numbers and symbols that look similar. For example, ‘L’ becomes ‘1’; ‘E’ becomes ‘3’; ‘T’ becomes ‘7’. (Hint: Take a look at String’s replace function. There is a version that accepts a lambda as the second parameter.)"
This is the function they're telling you to look at, specifically this one:
inline fun CharSequence.replace(
regex: Regex,
noinline transform: (MatchResult) -> CharSequence
): String
Returns a new string obtained by replacing each substring of this char sequence that matches the given regular expression with the result of the given function transform that takes MatchResult and returns a string to be used as a replacement for that match.
So the lambda you provide is a function that takes a MatchResult
and does something with it, and returns a CharSequence (which can be a one-character long String). The replace function calls that lambda for every match that regex makes.
You get the general idea of what you're supposed to do? You have two parts here - the thing that identifies parts of the input string to process, and the thing that takes those matches and changes them into something else. The result is the original string with those changes made. So you need to come up with a regex and a transform that work together.
Nobody (probably) is going to tell you the answer because the point is figuring it out for yourself, but if you have any questions about things like regexes people will be happy to help you out! And speaking of, this site is extremely useful (I just used it myself to check I knew what I was doing): https://regex101.com/
Here is the implementation as pointed by #cactustictacs :
5 -> {
mood = "leet"
val regex: Regex = """[LET]""".toRegex()
modifier = { message ->
message.uppercase().replace(regex) { m ->
when (m.value) {
"L" -> "1"
"E" -> "3"
"T" -> "7"
else -> ""
}
}
}
}
and here is the another method almost same but with minor change using regex.replace()
5 -> {
mood = "leet"
val regex: Regex = """[LET]""".toRegex()
modifier = { message ->
regex.replace(message.uppercase()){m ->
when (m.value) {
"L" -> "1"
"E" -> "3"
"T" -> "7"
else -> ""
}
}
}
}
You can use it in place of m to make it slightly more concise.
At the input I get a polynomial as a string, for example "7x^4+3x^3-6x^2+x-8". I want to get its coefficients in variables but I have no idea on how to do this. Maximum degree is not known, coefficients are integers. Also terms of some degree can be absent. I will be very grateful for any help.
I tried to split by "+" and "-" and then by "x^" but I have trouble with x, the term with (unwritten) degree 1.
Also I have tried firstly split by "x" then by "^" and handled exception with "-" but I don't know how to handle exception with missing degrees.
private fun koef(text: String) : List<Int> {
val vars = text.split("x")
val koefList = mutableListOf<Int>()
var count = 1
vars.forEach {
if (it == "-") koefList.add(-1)
else {
if (it[0] == '^')
}
}
return koefList
}
Here's one implementation.
It's slightly more general, allowing the terms to be in any order, and to have surrounding whitespace. But it still assumes that the polynomial is valid, that the powers are non-negative and all different, and that there's at least one term.
You didn't specify the order of coefficients, so this returns them in increasing power (starting with that of x^0, then x^1, &c).
private fun coeffs(polynomial: String): List<Int> {
val terms = polynomial.split(Regex("(?=[+-])")).associate{ term ->
val s = term.split(Regex("x\\^?"))
val coeff = s[0].replace(" ", "")
.let{ when (it){ "", "+" -> 1; "-" -> -1; else -> it.toInt() }}
val power = s.getOrNull(1)?.trim()
.let{ when (it){ null -> 0; "" -> 1; else -> it.toInt() }}
power to coeff
}
val highestPower = terms.keys.max()!!
return (0..highestPower).map{ terms[it] ?: 0 }
}
Sample results:
coeffs("x^2+2x-1") = [-1, 2, 1]
coeffs("2x^3 - 3x^4 - x + 4") = [4, -1, 0, 2, -3]
coeffs("x") = [0, 1]
coeffs("-2") = [-2]
It starts by splitting the string into terms. ((?=[+-]) is a lookahead, which matches an empty string if it's followed by + or -. Full documentation on Java/Kotlin regular expressions is here.)
It then splits each term into a coefficient and power, converts them to numbers, and creates a Map from term to coefficient. (That's quite awkward, as it has to handle several special cases where the numbers and/or signs are missing.) Using a map handles missing powers (and also powers that aren't in order).
Finally, it finds the largest power, and converts the map to a list of coefficients in increasing powers, filling in 0 for missing powers.
I've kept the code short, to show the principle. If it were to be used in production, you should probably make it safer and more efficient, e.g. by checking for invalid input such as empty string, invalid characters, or duplicate powers; and by putting the Regexs in properties so that they don't have to be recreated each time. Some unit tests wouldn't be a bad thing, either!
I am pretty confused with both functions fold() and reduce() in Kotlin, can anyone give me a concrete example that distinguishes both of them?
fold takes an initial value, and the first invocation of the lambda you pass to it will receive that initial value and the first element of the collection as parameters.
For example, take the following code that calculates the sum of a list of integers:
listOf(1, 2, 3).fold(0) { sum, element -> sum + element }
The first call to the lambda will be with parameters 0 and 1.
Having the ability to pass in an initial value is useful if you have to provide some sort of default value or parameter for your operation. For example, if you were looking for the maximum value inside a list, but for some reason want to return at least 10, you could do the following:
listOf(1, 6, 4).fold(10) { max, element ->
if (element > max) element else max
}
reduce doesn't take an initial value, but instead starts with the first element of the collection as the accumulator (called sum in the following example).
For example, let's do a sum of integers again:
listOf(1, 2, 3).reduce { sum, element -> sum + element }
The first call to the lambda here will be with parameters 1 and 2.
You can use reduce when your operation does not depend on any values other than those in the collection you're applying it to.
The major functional difference I would call out (which is mentioned in the comments on the other answer, but may be hard to understand) is that reduce will throw an exception if performed on an empty collection.
listOf<Int>().reduce { x, y -> x + y }
// java.lang.UnsupportedOperationException: Empty collection can't be reduced.
This is because .reduce doesn't know what value to return in the event of "no data".
Contrast this with .fold, which requires you to provide a "starting value", which will be the default value in the event of an empty collection:
val result = listOf<Int>().fold(0) { x, y -> x + y }
assertEquals(0, result)
So, even if you don't want to aggregate your collection down to a single element of a different (non-related) type (which only .fold will let you do), if your starting collection may be empty then you must either check your collection size first and then .reduce, or just use .fold
val collection: List<Int> = // collection of unknown size
val result1 = if (collection.isEmpty()) 0
else collection.reduce { x, y -> x + y }
val result2 = collection.fold(0) { x, y -> x + y }
assertEquals(result1, result2)
Another difference that none of the other answers mentioned is the following:
The result of a reduce operation will always be of the same type (or a super type) as the data that is being reduced.
We can see that from the definition of the reduce method:
public inline fun <S, T : S> Iterable<T>.reduce(operation: (acc: S, T) -> S): S {
val iterator = this.iterator()
if (!iterator.hasNext()) throw UnsupportedOperationException("Empty collection can't be reduced.")
var accumulator: S = iterator.next()
while (iterator.hasNext()) {
accumulator = operation(accumulator, iterator.next())
}
return accumulator
}
On the other hand, the result of a fold operation can be anything, because there are no restrictions when it comes to setting up the initial value.
So, for example, let us say that we have a string that contains letters and digits. We want to calculate the sum of all the digits.
We can easily do that with fold:
val string = "1a2b3"
val result: Int = string.fold(0, { currentSum: Int, char: Char ->
if (char.isDigit())
currentSum + Character.getNumericValue(char)
else currentSum
})
//result is equal to 6
reduce - The reduce() method transforms a given collection into a single result.
val numbers: List<Int> = listOf(1, 2, 3)
val sum: Int = numbers.reduce { acc, next -> acc + next }
//sum is 6 now.
fold - What would happen in the previous case of an empty list? Actually, there’s no right value to return, so reduce() throws a RuntimeException
In this case, fold is a handy tool. You can put an initial value by it -
val sum: Int = numbers.fold(0, { acc, next -> acc + next })
Here, we’ve provided initial value. In contrast, to reduce(), if the collection is empty, the initial value will be returned which will prevent you from the RuntimeException.
Simple Answer
Result of both reduce and fold is "a list of items will be transformed into a single item".
In case of fold,we provide 1 extra parameter apart from list but in case of reduce,only items in list will be considered.
Fold
listOf("AC","Fridge").fold("stabilizer") { freeGift, itemBought -> freeGift + itemBought }
//output: stabilizerACFridge
In above case,think as AC,fridge bought from store & they give stabilizer as gift(this will be the parameter passed in the fold).so,you get all 3 items together.Please note that freeGift will be available only once i.e for the first iteration.
Reduce
In case of reduce,we get items in list as parameters and can perform required transformations on it.
listOf("AC","Fridge").reduce { itemBought1, itemBought2 -> itemBought1 + itemBought2 }
//output: ACFridge
The difference between the two functions is that fold() takes an initial value and uses it as the accumulated value on the first step, whereas the first step of reduce() uses the first and the second elements as operation arguments on the first step.
When I run the code below I get a DataFrame with one bool column and two double columns. However, when I extract the boolcolumn as a Series the result is a Series object with types DateTime and float.
It looks like Deedle "cast" the column to another type.
Why is this happening?
open Deedle
let dates =
[ DateTime(2013,1,1);
DateTime(2013,1,4);
DateTime(2013,1,8) ]
let values = [ 10.0; 20.0; 30.0 ]
let values2 = [ 0.0; -1.0; 1.0 ]
let first = Series(dates, values)
let second = Series(dates, values2)
let third: Series<DateTime,bool> = Series.map (fun k v -> v > 0.0) second
let df1 = Frame(["first"; "second"; "third"], [first; second; third])
let sb = df1.["third"]
df1;;
val it : Frame<DateTime,string> =
Deedle.Frame`2[System.DateTime,System.String]
{ColumnCount = 3;
ColumnIndex = Deedle.Indices.Linear.LinearIndex`1[System.String];
ColumnKeys = seq ["first"; "second"; "third"];
ColumnTypes = seq [System.Double; System.Double; System.Boolean];
...
sb;;
val it : Series<DateTime,float> = ...
As the existing answer points out, GetColumn is the way to go. You can specify the generic parameter directly when calling GetColumn and avoid the type annotation to make the code nicer:
let sb = df1.GetColumn<bool>("third")
Deedle frame does not statically keep track of the types of the columns, so when you want to get a column as a typed series, you need to specify the type in some way.
We did not want to force people to write type annotations, because they tend to be quite long and ugly, so the primary way of getting a column is GetColumn where you can specify the type argument as in the above example.
The other ways of accessing column such as df?third and df.["third"] are shorthands that assume the column type to be float because that happens to be quite common scenario (at least for the most common uses of Deedle in finance), so these two notations give you a simpler way that "often works nicely".
You can use .GetColumn to extract the Series as a bool:
let sb':(Series<DateTime,bool>) = df1.GetColumn("third")
//val sb' : Series<DateTime,bool> =
//series [ 2013/01/01 0:00:00 => False; 2013/01/04 0:00:00 => False; 2013/01/08 0:00:00 => True]
As to your question of why, I haven't looked at the source, but I assume the type of indexer you use maybe returns an obj, then Deedle tries to cast it to something, or maybe it tries to cast everything to float.