Kotlin - Conditional method name with same parameters (mitigation of code duplication) - kotlin

Let's consider this code examples in Kotlin:
if (isFooBar)
method1(arg1, arg2, arg3)
else
method2(arg1, arg2, arg3)
// Or larger example
when(enumBar) {
VALUE_A -> method1(arg1, arg2, arg3)
VALUE_B -> method2(arg1, arg2, arg3)
VALUE_C -> method3(arg1, arg2, arg3)
VALUE_D -> method4(arg1, arg2, arg3)
else -> method5(arg1, arg2, arg3)
}
Can this be improved somehow to reduce code duplication? I imagine something like:
(if (isFooBar) method1 else method2)(arg1, arg2, arg3)

If the methods all have the same return type and parameter types, you can first select the right method to call and assign that to a val like this, using the function reference syntax.
val methodToCall =
if (isFooBar) ::method1 else ::method2
methodToCall(arg1, arg2, arg3)
In the case of when:
val methodToCall = when (someEnum) {
A -> ::method1
B -> ::method2
C -> ::method3
else -> ::method4
}
methodToCall(arg1, arg2, arg3)
Type inference will work correctly, provided that the methods do have the same parameter types and return types.
Technically, you don't need the methodToCall variable, and can just do:
(if (isFooBar) ::method1 else ::method2)(arg1, arg2, arg3)
but I don't think this is very readable.

Related

Convert from char to operator Kotlin

Is there any way to convert a char, lets say with a value of '+', into the operator +? Something like this:
println(1 charOperator 1);
output:
2
You can use something like this:
fun operatorFromChar(charOperator: Char):(Int, Int)->Int
{
return when(charOperator)
{
'+'->{a,b->a+b}
'-'->{a,b->a-b}
'/'->{a,b->a/b}
'*'->{a,b->a*b}
else -> throw Exception("That's not a supported operator")
}
}
and later call:
println(operatorFromChar('+').invoke(1,1))
Operators are, at the end of the way, functions. If you return a function with the operator's job, you can invoke it as it was the operator itself, but it will never be as "pretty" as calling the operator directly.
This isn't really possible. Maybe you should add your current solution and there's another way to help you out.
Here's a sneaky solution for calculating expressions with + and - only:
val exp = "10+44-12+3"
val result = exp.replace("-", "+-").split("+").sumBy { it.toInt() }
You can do something like
infix fun Int.`_`(that: Int) = this + that
where the backtick is unnecessary to this character but maybe necessary for other character. Then you can try:
println(2 _ 3) // output: 5
Update according to the comment:
I mean something like
val expr = input.split(' ')
when (expr[1])
{
'+' -> return expr[0].toInt() + expr[2].toInt()
'-' -> return expr[0].toInt() - expr[2].toInt()
'*' -> return expr[0].toInt() * expr[2].toInt()
'/' -> return expr[0].toInt() / expr[2].toInt()
// add more branches
}
However, I was wondering whether there is a better and tricky solution from the grammar of Kotlin.
What you basically want is an Char to result of an operation mapping. So, I decided to return the result right away and not a lambda.
fun Int.doOperation(charOperator: Char, x: Int) = when(charOperator) {
'+' -> this + x
'-' -> this - x
'/' -> this / x
'*' -> this * x
else -> throw IllegalArgumentException("Not supported")
}
Using an extension function maybe (?) makes the syntax a little nicer. You decide.
Call site:
println(5.doOperation('+', 6))
You can use the interpreter class in beanshell library
to convert string text automatically to result
for example
interpreter.eval("equal=2*3")
println(interpreter.get("equal").toString().toDouble().toString())
or can use expression class that does the same thing
fun String.stringToConditionalOperators(): (Boolean, Boolean) -> Boolean {
return when (this.lowercase(Locale.getDefault())) {
"and" -> {
{ a: Boolean, b: Boolean ->
a && b
}
}
"or" -> {
{ a: Boolean, b: Boolean ->
a || b
}
}
// You can add more operator 🤩
else -> {
return { a: Boolean, b: Boolean ->
a || b
}
}
}
}
Usage..
val operator = "AND"
operator.stringToConditionalOperators().invoke(one, two)

null to listOf(), not null to listOf(value) in Kotlin in one liner?

Let f() return a nullable value.
What I want to do is that
if f() is null, get an empty list,
else if f() is not null, get a list of the single item value.
In Scala, we can do something like this:
Option(f()).toList
or more verbosely
Option(f()).map(v => List(v)).getOrElse(List.empty)
In Kotlin, there is no Option (assuming no Funktionale library), and null does not have toList() unlike (None: Option) in Scala.
We have the Elvis operator, but null will be inside the listOf() function, so it will be
listOf(f() ?: /* What can I do here? */)
What we want for null is listOf(/*no argument */), but the Elvis operator requires an argument, so listOf(f() ?: ) will result in a compile error.
At least we can do
val v = f()
if (v == null) listOf() else listOf(v)
but it is a two liner.
Is there some expression for this?
Where I will use this expression is in the class's primary constructor default argument, so if it is not a one liner, it will be enclosed in brackets, so something like this:
class A(
val p1: List<V> = run {
val v = f()
if (v == null) listOf() else listOf(v)
},
val p2: ... = ...,
...)
This looks pretty ugly, isn't it?
EDIT
As #Naetmul pointed out, listOfNotNull(f()) is syntactically better to what I originally posted below, and also takes a variable number of arguments, for example
val myList = listOfNotNull(f(), g(), h())
will return a list of all the results that were not null.
I would use let here.
val myList = f()?.let { listOf(it) } ?: emptyList()
Use a ?. safe call on the return value of f(), then use let to run a code block. If f() is null, it won't run this block of code, resulting in a null value. Then we use the ?: elvis operator to fall back to an empty list.
Here it is broken up into several lines for a better understanding
val myValue = f()
val myList: List<Any>
if (myValue != null) {
myList = listOf(myValue)
} else {
myList = emptyList()
}
For this specific question, I can do
listOfNotNull(f())

How to repeat TryParse until success in F#?

Sorry for dumb question, but I'm perplexed a bit.
In C# I can idiomatically do the following:
int result = 0;
while (!Int32.TryParse(someString, out result))
{
...
}
In F# I have two options for TryDoSomething pattern.
It's either
let (isSuccess, result) = Int32.TryParse someString
or
let result = ref 0
let isSuccess = Int32.TryParse("23", result)
I can do while not Int32.TryParse("23", result) do ... but have no idea whether the same is achievable with the first variant.
P.S. Sure, tail recursion is also feasible here, but I'm interested in using while construct.
You can do :
while (not (fst (Int32.TryParse someString))) do
printfn "in while loop. It's not an Int32." ;
someString <- Console.ReadLine();
or (if you care about the parsed result) :
while
let (isSuccess, result) = Int32.TryParse someString in
not isSuccess do
printfn "in while loop. It's not an Int32 ; it is %A" result;
someString <- Console.ReadLine();

Objective C ^ Declare block in Swift [duplicate]

I am trying to rewrite from Objective-C to Swift, I cannot work out the syntax or understand the docs
Here is a simplified example in Objective-C I wrote:
[UIView animateWithDuration:10.0 animations:^{self.navigationController.toolbar.frame = CGRectMake(0,10,0,10);}];
How do I write this in Swift?
This is the template autocomplete gives:
UIView.animateWithDuration(duration: NSTimeInterval, animations: (() -> Void))
This is the swift closure format:
{(parameter:type, parameter: type, ...) -> returntype in
//do stuff
}
This is what you should do:
//The animation closure will take no parameters and return void (nothing).
UIView.animateWithDuration(duration: NSTimeInterval, animations: {() -> Void in
//Animate anything.
})
Here is the documentation for closures.
Since the expected argument types and return type to the animations argument are known the compiler can infer them without a problem. This should work (though I don't have the playground available right at the moment:
UIView.animateWithDuration(10.0, animations: {
self.navigationController.toolbar.frame = CGRect(x:0.0, y:10.0, width:10.0, height:0.0)
})
for more info about closures see the chapter in the swift docs
note about CGRect() - the developer docs show CGRect() being used in swift code. Perhaps it requires an import?
update for comments: you can also use a trailing closure like so:
UIView.animateWithDuration(10.0) {
self.navigationController.toolbar.frame = CGRect(x:0.0, y:10.0, width:10.0, height:0.0)
}
Following code can guide to write your own block.
class func testFunc(completion: ((list : NSArray!) -> Void)?) {
//--- block code.
if completion! != nil {
completion! (list: NSArray())
}
}
and you can call it like -
className.testFunc {
(list: NSArray!) -> Void in
}
You can basically write it in 3 identical ways:
write what to do right in the closure/code block:
UIView.animateWithDuration(10.0) {
self.navigationController.toolbar.frame = CGRect(x:0.0, y:10.0, width:10.0, height:0.0)
}
This is also known as trailing closure ( You can only do trailing closure if the closure parameter is the last parameter)
This doesn't mean the parameter 'animations' is no longer written. It is written but just as in the format of above.
Write exactly within the lines, most developers avoid such, because it's a little buggy to write with all the parenthesis and braces.
UIView.animateWithDuration(10.0, animations: {
self.navigationController.toolbar.frame = CGRect(x:0.0, y:10.0, width:10.0, height:0.0)
})
(Contrary to trailing closure you wrote name ie 'animations')
This is known as inline closure
Write in a more modular sense
UIView.animateWithDuration(duration: NSTimeInterval, animations: animatingFunc)
func animatingFunc() {
self.navigationController.toolbar.frame = CGRect(x:0.0, y:10.0, width:10.0, height:0.0)
}
Remember the type of the parameter 'animations' was () -> Void
Exactly as what we are doing, animatingFunc takes no parameters ie '()' and returns nothing ie 'void'
(In Swift, functions are types and can be passed in as parameters)
Some might say this is more readable some might say trailing closure is...
Side note1
You can also do nothing ( which really doesn't make sense but in many other handlers/animations/completion handlers you may not want to do anything)
UIView.animateWithDuration(duration: NSTimeInterval, animations: nil)
Side note2
Closures becomes more interesting when you have to capture a value. See this simple demonstration.
For more information about Swift closures see Apple's Documentation
How Do I Declare a Closure in Swift?
As a variable:
var closureName: (ParameterTypes) -> ReturnType
As an optional variable:
var closureName: ((ParameterTypes) -> ReturnType)?
As a type alias:
typealias ClosureType = (ParameterTypes) -> ReturnType
As a constant:
let closureName: ClosureType = { ... }
As a parameter to another function:
funcName(parameter: (ParameterTypes) -> ReturnType)
Note: if the passed-in closure is going to outlive the scope of the method, e.g. if you are saving it to a property, it needs to be annotated with #escaping.
As an argument to a function call:
funcName({ (ParameterTypes) -> ReturnType in statements })
As a function parameter:
array.sorted(by: { (item1: Int, item2: Int) -> Bool in return item1 < item2 })
As a function parameter with implied types:
array.sorted(by: { (item1, item2) -> Bool in return item1 < item2 })
As a function parameter with implied return type:
array.sorted(by: { (item1, item2) in return item1 < item2 })
As the last function parameter:
array.sorted { (item1, item2) in return item1 < item2 }
As the last parameter, using shorthand argument names:
array.sorted { return $0 < $1 }
As the last parameter, with an implied return value:
array.sorted { $0 < $1 }
As the last parameter, as a reference to an existing function:
array.sorted(by: <)
As a function parameter with explicit capture semantics:
array.sorted(by: { [unowned self] (item1: Int, item2: Int) -> Bool in return item1 < item2 })
As a function parameter with explicit capture semantics and inferred parameters / return type:
array.sorted(by: { [unowned self] in return $0 < $1 })
This site is not intended to be an exhaustive list of all possible uses of closures. ref: http://goshdarnclosuresyntax.com/

Best way to convert object to integer

I wish to safely convert an object from an external cache to an Integer type.
The only way I can seem to do this is inside a try catch block like so:
Try
Return Convert.ToInt32(obj)
Catch
'do nothing
End Try
I hate writing catch statements like this.
Is there a better way?
I have tried:
TryCast(Object, Int32)
Doesn't work (must be reference type)
Int32.TryParse(Object, result)
Doesn't work (must be a string type)
UPDATE
I like the comment posted by Jodrell - this would make my code look like this:
Dim cacheObject As Object = GlobalCache.Item(key)
If Not IsNothing(cacheObject) Then
If TypeOf cacheObject Is Int32 Then
Return Convert.ToInt32(cacheObject)
End If
End If
'Otherwise get fresh data from DB:
Return GetDataFromDB
Clarification: the question was originally tagged c# vb.net; the following applies to C# only (although may be translated into VB.NET):
If it is a boxed int, then:
object o = 1, s = "not an int";
int? i = o as int?; // 1, as a Nullable<int>
int? j = s as int?; // null
so generalising:
object o = ...
int? i = o as int?;
if(i == null) {
// logic for not-an-int
} else {
// logic for is-an-int, via i.Value
}
Unesscessary conversion to String should be avoided.
You could use Is to check the type beforehand
Dim value As Integer
If TypeOf obj Is Integer Then
value = DirectCast(obj, Integer)
Else
' You have a problem
End If
or,
You could implement a variation on TryCast like this,
Function BetterTryCast(Of T)(ByVal o As Object, ByRef result As T) As Boolean
Try
result = DirectCast(o, T)
Return True
Catch
result = Nothing
Return False
End Try
End Function
Which you could use like this
Dim value As Integer
If BetterTryCast(obj, value) Then
// It worked, the value is in value.
End If
The simplest one is
Int32.TryParse(anObject.ToString, result)
Every Object has a ToString method and calling Int32.TryParse will avoid a costly (in terms of perfomance) exception if you Object is not a numeric integer. Also the value for result, if the object is not a string will be zero.
EDIT. The answer from Marc Gravell raised my curiosity. Its answer seems complex for a simple conversion, but it is better? So I have tried to look at the IL code produced by its answer
object o = 1, s = "not an int";
int? i = o as int?; // 1, as a Nullable<int>
int? j = s as int?; // null
IL CODE
IL_0000: ldc.i4.1
IL_0001: box System.Int32
IL_0006: stloc.0 // o
IL_0007: ldstr "not an int"
IL_000C: stloc.1 // s
while the IL CODE produced by my answer is the following
IL_0000: ldc.i4.1
IL_0001: box System.Int32
IL_0006: stloc.0 // anObject
IL_0007: ldloc.0 // anObject
IL_0008: callvirt System.Object.ToString
IL_000D: ldloca.s 01 // result
IL_000F: call System.Int32.TryParse
Definitively the answer from Marc is the best approach. Thanks Marc to let me discover something new.
this works:
Int32.TryParse(a.ToString(), out b);