I'd like to simplify this expression, especially "isDigit" and "isLetter" case. How to do it?
smoothInput.forEach { char ->
when {
char.isValidOperator() -> {
output.push(char)
}
char.isDigit() -> {
if (output.isNotEmpty() && output.last()!!.isNumeric()) output.addToLast(char)
else output.push(char)
}
char.isLetter() -> {
if (output.isNotEmpty() && output.last()!!.isValidVariableName()) output.addToLast(char)
else output.push(char)
}
else -> {
throw InvalidIdentifierException()
}
}
}
I think, that it isn't important, but it's much better to add code here than in comment
output is InputStack Type:
class InputStack : Stack<String> {
override val storage = mutableListOf<String>()
fun push(e: Char) = push(e.toString())
fun push(e: Operator) = push(e.toString())
fun addToLast(e: Char) {
storage[storage.size - 1] += e.toString()
}
}
Stack Interface:
interface Stack<T> {
val storage: MutableList<T>
fun asString(): String = buildString {
appendLine("----top----")
storage.asReversed().forEach {
appendLine(it)
}
appendLine("-----------")
}
fun push(element: T) = storage.add(element)
fun pop(): T {
if (storage.size == 0) throw EmptyStackException()
return storage.removeAt(storage.size - 1)
}
fun isEmpty(): Boolean = storage.isEmpty()
fun isNotEmpty(): Boolean = !isEmpty()
fun last(): T? = storage.lastOrNull()
fun forEach(action: (T) -> Unit) {
for (element in storage) action(element)
}
}
You can extract some common parts in the following way:
fun addCharToOutputConditionally(char: Char, output: InputStack, conditionOnLast: (String) -> Boolean) {
if (output.isNotEmpty() && conditionOnLast(output.last()!!)) output.addToLast(char)
else output.push(char)
}
smoothInput.forEach { char ->
when {
char.isValidOperator() -> {
output.push(char)
}
char.isDigit() -> {
addCharToOutputConditionally(char, output) {
it.isNumeric()
}
}
char.isLetter() -> {
addCharToOutputConditionally(char, output) {
it.isValidVariableName()
}
}
else -> {
throw InvalidIdentifierException()
}
}
}
However, in cases like this, I don't think it's usually worth spending the time to refactor it this way, considering that there's little to gain by doing so: the resulting code is even longer and arguably harder to read than the original one.
The new when expression:
smoothInput.forEach { char ->
when {
char.isValidOperator() -> output.push(char)
char.isDigit() -> output.addToLastConditionally(char) { it.isNumeric() }
char.isLetter() -> output.addToLastConditionally(char) { it.isValidVariableName() }
else -> throw InvalidIdentifierException()
}
}
I've change the addToLast function in InputStack for addToLastConditionally
fun addToLastConditionally(char: Char, condition: (String) -> Boolean) {
if (isNotEmpty() && condition(last()!!)) storage[storage.size - 1] += char.toString()
else push(char)
}
Related
If I have a string that has an arithmetic expression with paranthesis like:
((4 * 7) / 2) - 7
How do I evaluate it automatically? In particular with Kotlin. I heard that you need to make a parser, so how can I do so in Kotlin and have all the necessary basic operations in the example as such?
fun evaluate(str: String): Double {
data class Data(val rest: List<Char>, val value: Double)
return object : Any() {
fun parse(chars: List<Char>): Double {
return getExpression(chars.filter { it != ' ' })
.also { if (it.rest.isNotEmpty()) throw RuntimeException("Unexpected character: ${it.rest.first()}") }
.value
}
private fun getExpression(chars: List<Char>): Data {
var (rest, carry) = getTerm(chars)
while (true) {
when {
rest.firstOrNull() == '+' -> rest = getTerm(rest.drop(1)).also { carry += it.value }.rest
rest.firstOrNull() == '-' -> rest = getTerm(rest.drop(1)).also { carry -= it.value }.rest
else -> return Data(rest, carry)
}
}
}
fun getTerm(chars: List<Char>): Data {
var (rest, carry) = getFactor(chars)
while (true) {
when {
rest.firstOrNull() == '*' -> rest = getTerm(rest.drop(1)).also { carry *= it.value }.rest
rest.firstOrNull() == '/' -> rest = getTerm(rest.drop(1)).also { carry = it.value / carry }.rest
else -> return Data(rest, carry)
}
}
}
fun getFactor(chars: List<Char>): Data {
return when (val char = chars.firstOrNull()) {
'+' -> getFactor(chars.drop(1)).let { Data(it.rest, +it.value) }
'-' -> getFactor(chars.drop(1)).let { Data(it.rest, -it.value) }
'(' -> getParenthesizedExpression(chars.drop(1))
in '0'..'9', ',' -> getNumber(chars)
else -> throw RuntimeException("Unexpected character: $char")
}
}
fun getParenthesizedExpression(chars: List<Char>): Data {
return getExpression(chars)
.also { if (it.rest.firstOrNull() != ')') throw RuntimeException("Missing closing parenthesis") }
.let { Data(it.rest.drop(1), it.value) }
}
fun getNumber(chars: List<Char>): Data {
val s = chars.takeWhile { it.isDigit() || it == '.' }.joinToString("")
return Data(chars.drop(s.length), s.toDouble())
}
}.parse(str.toList())
}
val expresssion = "((4 * 7) / 2) - 7"
val result = evaluate(expresssion)
println(result) // Output: 7.0
Look up "Math parser" on google. These are various dependencies you can add that allow you to then parse string input into mathematical formulas.
converting java to kotlin,
java code
private boolean hasEndpoint() {
if (mSettings == null || mSettings.getEndpoint() == null) {
if (isDebugMode()) {
throw new IllegalArgumentException("endpoint is not set !!!");
}
return false;
}
return true;
}
public void doAction_1(...) {
if (!hasEndpoint()) {
callback.onError(ENDPOINT_UNINITIALIZED, "ERROR_END_POINT_NOT_SET");
return;
}
//do the action with valid endpoint
doSomething_1(mSettings.getEndpoint());
}
the kotlin:
private fun hasEndpoint(): Boolean {
if (mSettings?.endpoint == null) {
require(!isDebugMode) { "endpoint is not set !!!" }
return false
}
return true
}
fun doAction_1() {
if (!hasEndpoint()) {
callback.onError(ENDPOINT_UNINITIALIZED, "ERROR_END_POINT_NOT_SET")
return
}
//do the action with valid endpoint
doSomething_1(mSettings!!.getEndpoint());
}
There are multiple functions (i.e. doAction_1(), doAction_2() ...) doing the same check using hasEndpoint().
What is Kotlin idiomatic way to do something like this?
You can use a concept similar to Python decorators:
// Add your check here
fun withCheck(action: () -> Unit) {
if (!hasEndpoint()) {
callback.onError(ENDPOINT_UNINITIALIZED, "ERROR_END_POINT_NOT_SET")
return
}
action()
}
// Add your actions enclosed with `withCheck`
fun action1() = withCheck {
doSomething_1(mSettings!!.getEndpoint());
}
fun action2() = withCheck {
doSomething_2(mSettings!!.getEndpoint());
}
You can use a property instead of a function for hasEndpoint or rather hasNoEndpoint and use when in place of if else
private val hasNoEndpoint: Boolean
get() = when {
mSettings?.endpoint != null -> false
isDebugMode -> throw IllegalArgumentException("endpoint is not set !!!")
else -> true
}
// use this in withCheck function as in enzo's answer
fun withEndpoint(action: () -> Unit): Unit = when {
hasNoEndpoint -> callback.onError(ENDPOINT_UNINITIALIZED, "ERROR_END_POINT_NOT_SET")
else -> action()
}
i have a error on code that says
overload resolution ambiguity. all these functions match
class MovieRespository (val apiService: ApiService, val movieDao: MovieDao) {
fun getListMovie() = movieDao.streamAll()
.onErrorResumeNext{
apiService.getMyMovie()
.doOnSuccess {
if (it.results.isEmpty()){
}else{
movieDao.deleteAll()
it.results.let {
Timber.d("input data")
val semuadata = it.map { data -> Movie.from(data) }
movieDao.insert(semuadata)
}
}
}
}
}
also there something like this in my error
enter image description here
You simply have to specify the parameter you take in onErrorResumeNext:
i have a error on code that says
overload resolution ambiguity. all these functions match
class MovieRespository (val apiService: ApiService, val movieDao: MovieDao) {
fun getListMovie() = movieDao.streamAll()
.onErrorResumeNext{ next: Publisher<List<Movie>> ->
apiService.getMyMovie()
.doOnSuccess {
if (it.results.isEmpty()){
}else{
movieDao.deleteAll()
it.results.let {
Timber.d("input data")
val semuadata = it.map { data -> Movie.from(data) }
movieDao.insert(semuadata)
}
}
}
}
}
I have the following method:
internal typealias MaybeError<T> = Either<GenericError, T>
override fun createCompany(companyDomain: CompanyDomain): MaybeError<CompanyDomain> =
checkCompany(companyDomain).map { it.toEntity() }.fold({ Either.left(it) }) { company ->
with (companyRepository) {
isCompanyExists(company).fold({ Either.left(it) }) { isExists ->
if (isExists) return#with Either.left(CompanyNameExists(companyDomain))
createCompany(company).fold({ Either.right(companyDomain) }) { Either.left(it) }
}
}
}
Is there a better/more idiomatic way to write this using Arrow?
It is hard to refactor because I can only assume what used methods should return. But I guess the methods returns MaybeError. In this case we can omit fold({ Either.left(it) }) and we can use map or flatMap.
internal typealias MaybeError<T> = Either<GenericError, T>
override fun createCompany(companyDomain: CompanyDomain): MaybeError<CompanyDomain> =
checkCompany(companyDomain)
.map { it.toEntity() }
.flatMap { company ->
companyRepository.isCompanyExists(company)
.flatMap { isExists ->
if (isExists) {
MaybeError.left(CompanyNameExists(companyDomain))
} else {
companyRepository.createCompany(company)
}
}
}
Try in rxJava2 Kotlin combine Single with Flowable but nothing not happening:
Does not undrstand what wrong
Flowable.create<Int>({ emmit ->
loadNewListener = object :Listener {
override fun onEmit(id: Int) {
emmit.onNext(id)
}
}
}, BackpressureStrategy.LATEST)
.debounce(500, TimeUnit.MILLISECONDS)
.flatMapSingle {
loadNew(id = it.id)
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ (data:Data) ->
}, {
Timber.e("Failed load data ${it.message}")
})
my method is returning Single:
private fun loadNew(id: Int): Single<Data> {
return when (pdfType) {
CASE_0 -> {
Single.create<Data> { emmit ->
service.get("data")
.enqueue(
object : Callback<Void> {
override fun onFailure(call: Call<Void>?, t: Throwable?) {
// failure
}
override fun onResponse(call: Call<Void>?, response: Response<Void>?) {
emmit.onSuccess(it.data)
}
}
}//single
}//case_0
CASE_1 -> 1Repository.loadsome1Rx(id = id).map { it.getData() }
CASE_2 -> 2Repository.loadsom2LocalRx(id = id).map { it.getData() }
else -> {
throw java.lang.RuntimeException("$this is not available type!")
}
}
What is wrong im my code?
Need Maby call Single in Flowable subscribe() seppurate
like this?
Flowable.create<Int>({ emmit ->
loadNewListener = object :Listener {
override fun onEmit(id: Int) {
emmit.onNext(id)
}
}
}, BackpressureStrategy.LATEST)
.debounce(500, TimeUnit.MILLISECONDS)
.subscribe({
loadNew(id = it.id)
}, {
Timber.e("")
})
This code is workin but looks not simple as via combine try.
This simple example based on your code is working
var i = 0
fun foo() {
Flowable.create<Int>({ emmit ->
emmit.onNext(i)
i++
}, BackpressureStrategy.LATEST)
.debounce(500, TimeUnit.MILLISECONDS)
.flatMapSingle {
Single.create<String> { emmit ->
emmit.onSuccess("onSuccess: $it")
}
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
Log.i("RX", "Subscribe: $it")
}, {
it.printStackTrace()
})
}
Check SingleEmitter.onSuccess() and SingleEmitter.onError() is called in all cases in when (pdfType)...
As #Stas Bondar said in answer below This simple example based on your code is working!!
Problem was in loadNewListener .
It does not init in time and has null value when need. Call create Flowable on init ViewModel but loadNewListener did not have time to create when i call him from fragment.
loadNewListener = object :Listener{...}
Becuse need some time mutch for init rxJava expression!
And combine flowable with single via flatMapSingle spent more time than just call single on flowable dubscrinbe!
So use temp field:
private var temp: Temp? = null
fun load(id: Int) {
loadNewListener.apply {
when {
this != null -> load(id = id)
else -> userEmitPdfTemp = Temp(id = id)
}
}
}
Flowable.create<Data>({ emmit ->
userEmitPdfTemp?.let {id->
emmit.onNext(Data(id))
userEmitPdfTemp =null
}
loadNewListener = object :Listener {
override fun load(id: Int) {
emmit.onNext(Data(id))
}
}
}