I am a java developer and an trying to conditionally return from a method in scala.
def parseDates: Boolean = {
var date = aoc4Xml \\ "FinancialStmtFromDate" text
if (StringUtils.isEmpty(date)) {
addErrorStringFromString("Please enter the financial year start date")
return false
}
aoc4Dto.finYearStartDate = DateUtils.getDateFromFormatOfString(date)
date = aoc4Xml \\ "FinancialStmtToDate" text
if (StringUtils.isEmpty(date)) {
addErrorStringFromString("Please enter the financial year end date")
return false
}
aoc4Dto.finYearEndDate = DateUtils.getDateFromFormatOfString(date)
true
}
This does not compile, at the first if block saying illegal start of expression. How do I implement the above where returning early from a method was considered the best way to handle things.
Just to clarify things:
The above problem technically reduces to
def test(testString:String) = {
if(testString == "Chennai"){
println("correct")
return true
}
println("outside if")
false
}
test("Chennai")
The above will not compile at line return true. There is no problem with the first xml xpath and text.. please ignore all of it and check the conditional return from the method.
Next time please submit codes without the need to stubb a lot of functionality. The stabbed snipeet, that compiles:
object StringUtils{
def isEmpty(s: String) = true
}
object aoc4Xml{
def \\(s: String) = {
new {def text = "test"}
}
}
object DateUtils{
def getDateFromFormatOfString(s: String) = 0
}
object Main {
val date = "date"
def addErrorStringFromString(s: String) = println(s)
object aoc4Dto{
var finYearStartDate = 0
var finYearEndDate = 0
}
def parseDates: Boolean = {
var date = (aoc4Xml \\ "FinancialStmtFromDate" text)
if (StringUtils.isEmpty(date)) {
addErrorStringFromString("Please enter the financial year start date")
return false
}
aoc4Dto.finYearStartDate = DateUtils.getDateFromFormatOfString(date)
date = (aoc4Xml \\ "FinancialStmtToDate" text)
if (StringUtils.isEmpty(date)) {
addErrorStringFromString("Please enter the financial year end date")
return false
}
aoc4Dto.finYearEndDate = DateUtils.getDateFromFormatOfString(date)
true
}
}
The difference(just enclose with bracers):
var date = (aoc4Xml \\ "FinancialStmtFromDate" text)
date = (aoc4Xml \\ "FinancialStmtToDate" text)
And your second snippet has another error:
def test(testString:String): Boolean = {
if(testString == "Chennai"){
println("correct")
return true
}
println("outside if")
false
}
just provide returning type:
def test(testString:String): Boolean = {
Related
I am trying to print by making it a string in the class. I am trying not to have any print lines in the class at all. I can't figure out how to not have the print lines in my printBoard method.
class MakeString() {
fun printThis(): String {
var line = arrayOf("hello","printMe")
var addThis = "there"
for (element in array) {
println()
}
return line.toString()
}
override fun toString(): String {
return """
${printThis()}
""".trimIndent()
}
}
You can use a StringBuilder to build the output string:
class Puzzle(var rows :Int,var cols: Int) {
fun printBoard(): String {
var emptyCell = '.'
var board = Array(rows) { Array(cols) { emptyCell } }
val builder = StringBuilder()
for (row in 0 until board.size) {
for (col in 0 until board[row].size) {
builder.append(board[row][col])
}
builder.append('\n')
}
return builder.toString()
}
override fun toString(): String {
return """
${printBoard()}
""".trimIndent()
}
}
fun main () {
var wordss = Puzzle(45, 45)
println(wordss.printBoard())
}
I created a fragment that in the onActivityCreated method fetches Firebase data by limiting the query to a calendar date. Then I place Observers on my LiveData that are inside my ViewModel and that will deliver the list to my Adapter.
If I add, remove or update items in the same list, the changes are sent to firebase and the adapter reflects them on the screen. It works ok.
But, I am trying to develop a filter button, which will basically change the deadline date for the Firebase query. When I select a particular filter, the viewModel needs to retrieve the data from Firebase limited to the filter date. This generates a new list, having a different size from the previous one.
However, when the query occurs, the Adapter's getItemCount() method stores the size of the last list. This fact confuses the Adapter and the functions notifyItemInserted and notifyItemRemoved end up making confusing animations on the screen after changing the filter. I dont know whats is wrong.
How can I correctly observes LiveData and tell the adapter? Am I making a mistake in the MVVM architecture or forgetting some function?
My Fragment:
class HistoryFragment : Fragment(), OnItemMenuRecyclerViewClickListener {
private lateinit var mSecurityPreferences: SecurityPreferences
private lateinit var viewModel: BalancesViewModel
private lateinit var adapter: BalancesAdapter
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
setHasOptionsMenu(true)
viewModel = ViewModelProvider(this).get(BalancesViewModel::class.java)
adapter = BalancesAdapter(requireContext())
mSecurityPreferences = SecurityPreferences(requireContext())
return inflater.inflate(R.layout.fragment_history, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
setupFilter()
//Setup adapter
adapter.listenerMenu = this
recycler_view_history.adapter = adapter
//Fetch data based in filter by date
viewModel.fetchBalances(mSecurityPreferences.getStoredLong(FILTER_DATE))
// Put logic to listen RealTimeUpdates
viewModel.getRealTimeUpdates(mSecurityPreferences.getStoredLong(FILTER_DATE))
viewModel.balances.observe(viewLifecycleOwner, Observer {
adapter.setBalances(it)
})
viewModel.balance.observe(viewLifecycleOwner, Observer {
adapter.addBalance(it)
})
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.history_menu_filter, menu)
super.onCreateOptionsMenu(menu, inflater)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.item_menu_filter_this_month -> {
updateFilter(THIS_MONTH)
}
R.id.item_menu_filter_two_months -> {
updateFilter(TWO_MONTHS)
}
R.id.item_menu_filter_last_six_months -> {
updateFilter(LAST_SIX_MONTHS)
}
R.id.item_menu_filter_all -> {
updateFilter(ALL_MONTHS)
}
}
return super.onOptionsItemSelected(item)
}
private fun setupFilter() {
var filterOption = mSecurityPreferences.getStoredLong(FILTER_DATE)
if (filterOption == 0L){
filterOption = HandleDate.getLongToFilter(LAST_SIX_MONTHS)
mSecurityPreferences.storeLong(FILTER_DATE, filterOption)
}
}
private fun updateFilter(filterOption: Int){
val newFilterOption = HandleDate.getLongToFilter(filterOption)
mSecurityPreferences.storeLong(FILTER_DATE, newFilterOption)
updateUI()
}
private fun updateUI(){
viewModel.fetchBalances(mSecurityPreferences.getStoredLong(FILTER_DATE))
viewModel.getRealTimeUpdates(mSecurityPreferences.getStoredLong(FILTER_DATE))
}
}
My ViewModel:
class BalancesViewModel : ViewModel() {
private val userReference = FirebaseAuth.getInstance().currentUser!!.uid
private val dbUserReference = FirebaseDatabase.getInstance().getReference(userReference)
private val _balances = MutableLiveData<List<Balance>>()
val balances: LiveData<List<Balance>>
get() = _balances
private val _balance = MutableLiveData<Balance>()
val balance: LiveData<Balance>
get() = _balance
private val _result = MutableLiveData<Exception?>()
val result: LiveData<Exception?>
get() = _result
fun addBalance(balance: Balance) {
balance.id = dbUserReference.push().key
dbUserReference.child(NODE_BALANCES).child(balance.id!!).setValue(balance)
.addOnCompleteListener {
if (it.isSuccessful) {
_result.value = null
} else {
_result.value = it.exception
}
}
}
private val childEventListener = object : ChildEventListener {
override fun onCancelled(error: DatabaseError) {
}
override fun onChildMoved(snapshot: DataSnapshot, p1: String?) {
}
override fun onChildChanged(snapshot: DataSnapshot, p1: String?) {
val balance = snapshot.getValue(Balance::class.java)
balance?.id = snapshot.key
_balance.value = balance
}
override fun onChildAdded(snapshot: DataSnapshot, p1: String?) {
val balance = snapshot.getValue(Balance::class.java)
balance?.id = snapshot.key
_balance.value = balance
}
override fun onChildRemoved(snapshot: DataSnapshot) {
val balance = snapshot.getValue(Balance::class.java)
balance?.id = snapshot.key
balance?.isDeleted = true
_balance.value = balance
}
}
fun getRealTimeUpdates(longLimitDate: Long) {
dbUserReference.child(NODE_BALANCES).orderByChild(COLUMN_DATE_MILLI)
.startAt(longLimitDate.toDouble()).addChildEventListener(childEventListener)
}
fun fetchBalances(longLimitDate: Long) {
dbUserReference.child(NODE_BALANCES).orderByChild(COLUMN_DATE_MILLI)
.startAt(longLimitDate.toDouble())
.addListenerForSingleValueEvent(object : ValueEventListener {
override fun onCancelled(error: DatabaseError) {}
override fun onDataChange(snapshot: DataSnapshot) {
if (snapshot.exists()) {
val listBalances = mutableListOf<Balance>()
for (balanceSnapshot in (snapshot.children)) {
val balance = balanceSnapshot.getValue(Balance::class.java)
balance?.id = balanceSnapshot.key
balance?.let { listBalances.add(it) }
}
listBalances.sortByDescending { it.dateMilli }
_balances.value = listBalances
}
}
})
}
fun updateBalance(balance: Balance) {
dbUserReference.child(NODE_BALANCES).child(balance.id!!).setValue(balance)
.addOnCompleteListener {
if (it.isSuccessful) {
_result.value = null
} else {
_result.value = it.exception
}
}
}
fun deleteBalance(balance: Balance) {
dbUserReference.child(NODE_BALANCES).child(balance.id!!).setValue(null)
.addOnCompleteListener {
if (it.isSuccessful) {
_result.value = null
} else {
_result.value = it.exception
}
}
}
My Adapter:
class BalancesAdapter(private val context: Context) :
RecyclerView.Adapter<BalancesAdapter.BalanceViewModel>() {
private var balances = mutableListOf<Balance>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
BalanceViewModel(
LayoutInflater.from(parent.context)
.inflate(R.layout.item_recyclerview_balance, parent, false)
)
override fun getItemCount() = balances.size
override fun onBindViewHolder(holder: BalanceViewModel, position: Int) {
holder.view.text_view_value_balance_item.text = balances[position].value
holder.view.text_view_date_item.text = balances[position].date
}
fun setBalances(balances: List<Balance>) {
this.balances = balances as MutableList<Balance>
notifyDataSetChanged()
}
fun addBalance(balance: Balance) {
val index = balances.indexOf(balance)
if (!balances.contains(balance)) {
balances.add(balance)
notifyItemInserted(index)
} else {
if (balance.isDeleted) {
balances.removeAt(index)
notifyItemRemoved(index)
} else {
balances[index] = balance
}
}
notifyItemRangeChanged(index, itemCount)
}
class BalanceViewModel(val view: View) : RecyclerView.ViewHolder(view)
}
Tnks for your attention.
Okay, it's been 4 days since I asked this question and after feeling a little frustrated with the project I come back here on StackOverFlow to post my own answer.
The problematic issue within the code I showed is in my Adapter's addBalance method.
When I created the Balance data model, I set the isDeleted attribute to identify that it was deleted. Upon entering Firebase it receives a NULL value and therefore it ceases to exist.
Then, as I have two listeners (one defined in the addListenerForSingleValueEvent method and the other defined in the addChildEventListener method), one ends up triggering the other when there is a change in the Firebase data, but I don't want to go into detail on that issue. The fact is that I checked that my addBalance method was being called after I deleted an object, causing that object to be inserted back into the Adapter's data list, even before the removal operation ended in Firebase.
So I changed the logic of my method to make sure that my object was deleted and only included it in my Adapter list after checking the isDeleted attribute.
fun dealWithBalance(balance: Balance){
val index = balances.indexOf(balance)
if(balance.isDeleted && balances.contains(balance)){
balances.removeAt(index)
notifyItemRemoved(index)
} else if(!balance.isDeleted && !balances.contains(balance)){
balances.add(balance)
} else if(index >= 0){
balances[index] = balance
notifyItemChanged(index)
}
}
I renamed addBalance to dealWithBalance...
Here's the code in Kotlin.
file1
fun main() {
var player = Player("madrigal")
println(player.name)
printPlayerStatus(player)
println(player.name)
}
private fun printPlayerStatus(player: Player) {
println("${player.name} ")
}
file2
package com.bignerdranch.nyethack
import java.io.File
class Player(
_name: String,
var healthPoints: Int = 100,
var isBlessed: Boolean,
private val isImmortal: Boolean
) {
constructor(name: String) : this(name, isBlessed = true, isImmortal = false) {
if (name.toLowerCase() == "kar") healthPoints = 40
}
var name = _name
get() ="${field.capitalize()} of $hometown"
private set(value) {
field = value.trim()
}
val hometown: String = selectHometown()
init {
require(healthPoints > 0, { "healthPoints must be greater than zero." })
require(name.isNotBlank(), { "Player must have a name" })
}
private fun selectHometown(): String = File("data/towns.txt")
.readText()
.split('\n')
.shuffled()
.last()
fun castFireball(numFireballs: Int = 2) =
println("A glass of Fireball springs into existence. (x$numFireballs)")
fun formatHealthStatus() =
when (healthPoints) {
100 -> "is in excellent condition!"
in 90..99 -> "has a few scratches."
in 75..89 -> if (isBlessed) {
"has some minor wounds, but is healing quite quickly!"
} else {
"has some minor wounds."
}
in 15..74 -> "looks pretty hurt."
else -> "is in awful condition!"
}
fun auraColor(): String {
val auraVisible = isBlessed && healthPoints > 50 || isImmortal
val auraColor = if (auraVisible) "GREEN" else "NONE"
return auraColor
}
}
When I run it I get the results:
Madrigal of Boston
Madrigal of Boston
However, I expect to get the results without the empty string in the middle. Like this:
Madrigal of Boston
Madrigal of Boston
Madrigal of Boston
Then I rewrite the function printPlayerStatus as below :
private fun printPlayerStatus(player: Player) {
println(player.name)
}
Now the output is correct.
Actually I copy the code from the book. And according to the book the code should work fine.
Please, help me to understand why it happens and find my mistake.
Problem is solved. If I avoid working with file and make a simple list of strings, the output gets as expected.
In the code below, I'm confused as to where the return statement in the code returns to? When executed, it works as expected, but does it return to:
if userIsInTheMiddleOfTyping == true
or does it return to:
if let digit = sender.currentTitle
Below is the full chunk of code where this applies.
class ViewController: UIViewController {
private var userIsInTheMiddleOfTyping = false
private var decimalUsed = false
#IBAction private func touchDigit(sender: UIButton)
{
if let digit = sender.currentTitle {
if userIsInTheMiddleOfTyping == true {
if digit == "." && decimalUsed == true {
return //where does this return to?
} else if digit == "." && decimalUsed == false {
decimalUsed = true
}
let textCurrentlyInDisplay = display.text!
display.text = textCurrentlyInDisplay + digit
} else {
display.text = digit
}
userIsInTheMiddleOfTyping = true
}
}
A return always returns out of the function, so in this case it returns to the line of code that calls touchDigit(...)
Basically here, the return just stops the execution of the touchDigit function.
(Which means that none of the code following the return will be run)
The return simply stops the code. You can put it in functions if you would like. For example:
If I want to continue running some code only if a certain statement is true, then you can return the function to stop it if it is false.
func something(a: Int, b: Int) {
if a != b {
return//Stops the code
}
//Some more code -- if a is not equal to b, this will not be called
}
Remember, this only works with void functions. It can work with others as well, but that is slightly different. You must return something along with it. Another example:
func somethingElse(a: Int, b: Int) -> Bool{
if a != b {
return false //stops the code, but also returns a value
}
return true //Will only get called if a == b
}
In this function, it is return a Boolean. If a != b, then return false is written because that returns false while also stoping the code.
For more on returns, you can visit Apple's documentation on functions.
I want to get the parameter types of a Haxe function using a macro and convert them to a shorthand string form, a bit like JNI/Java method signatures, but without a return type.
The motivation here is to provide access to the function parameter types, without having to slowly search through run-time type information at runtime. For example, say you want to construct a graphical widget for calling a function that takes parameters. You will need the type of each function parameter to create the correct spinbox, textbox, and select box widgets needed for tweaking the values that will be passed to the function.
So the question is, how can you save Haxe function parameter types with a macro?
Here is a macro that works for a few basic types, and any abstracts based on those types. It maps the function parameter types to strings. For example, function type String->Float->Int->String->Void maps to sfis, Float->Float->Int to ff etc:
package;
import haxe.macro.Expr;
import haxe.macro.Context;
import haxe.macro.Type;
import haxe.macro.ExprTools;
// Map some Haxe types to string ids
#:enum abstract TypeMapping(String) from (String) {
var BOOL = "b";
var FLOAT = "f";
var INT = "i";
var STRING = "s";
}
class Util
{
public macro static function getParameterTypes(f:Expr):ExprOf<String> {
var type:Type = Context.typeof(f);
if (!Reflect.hasField(type, 'args')) {
throw "Parameter has no field 'args'";
}
var t = type.getParameters()[0];
var args:Array<Dynamic> = Reflect.field(type, 'args')[0];
var signature:String = "";
for (i in 0...args.length) {
switch(args[i].t) {
case TAbstract(t, p):
var underlyingTypeName = Std.string(t.get().type.getParameters()[0]);
switch(underlyingTypeName) {
case "Bool":
signature += TypeMapping.BOOL;
case "Float":
signature += TypeMapping.FLOAT;
case "Int":
signature += TypeMapping.INT;
case "String":
signature += TypeMapping.STRING;
default:
throw "Unhandled abstract function parameter type: " + underlyingTypeName;
}
case CString:
signature += TypeMapping.STRING;
default:
throw "Unhandled function parameter type: " + args[i];
}
}
return macro $v{signature};
}
}
A further problem is how to make this work for all types, rather than just ones you handle explicitly. To do that, you might populate an array of Strings with the type name/class name/path of each function parameter instead, and return that instead of a single String. Here's an attempt at that, note it doesn't work with function parameters (and probably other stuff) yet:
public macro static function getFullParameterTypes(f:Expr):ExprOf<Array<String>> {
var type:Type = Context.typeof(f);
if (!Reflect.hasField(type, 'args')) {
throw "Parameter has no field 'args'";
}
var args:Array<Dynamic> = Reflect.field(type, 'args')[0];
var pos = haxe.macro.Context.currentPos();
var signature:Array<Expr> = [];
for (i in 0...args.length) {
var argType:Type = args[i].t;
var s;
switch(argType) {
case TFun(t, r):
s = EConst(CString("Function"));
throw "Not working with function parameters yet";
case _:
s = EConst(CString(argType.getParameters()[0].toString()));
}
signature.push({expr: s, pos: pos});
}
return macro $a{signature};
}
A more up to date approach..
macro function deflate(fun:haxe.macro.Expr) {
var type = haxe.macro.Context.typeof(fun);
final paramNames = extractFunction(type);
return macro $v{paramNames};
}
// Extract function parameter names
function extractFunction(type):Array<Dynamic> {
return switch type {
case TFun(args, ret): {
var paramNames:Array<Dynamic> = [];
for (p in args) {
final pName = p.name;
paramNames.push(pName);
}
return paramNames;
}
case _: {throw "unable to extract function information";};
}
}
Use it like this
using Macros;
function func(name:String, greeting:String){};
final args = fun.deflate();
trace(args) // output: [name, greeting]
A problem you may face is how to collect the default value of a parameter, consider the example below.
function func(name:String = "Josh", greeting:String = "Hello"){ return '$greeting $name'};
final args = fun.deflate();
trace(args) // output: [name, greeting]
Now let's account for default parameter values by slightly modifying the code:
// Extract function parameter names
function extractFunction(type):Array<Dynamic> {
return switch type {
case TFun(args, ret): {
var paramNames:Array<Dynamic> = [];
for (p in args) {
final pName = p.name;
final v = {name: pName, value: null}; // <= anticipate a value
paramNames.push(v);
}
return paramNames;
}
case _: {throw "unable to extract function information";};
}
}
macro function deflate(fun:haxe.macro.Expr) {
var type = haxe.macro.Context.typeof(fun);
final paramNames:Array<Dynamic> = extractFunction(type);
// extract default param values
switch fun.expr {
case EFunction(f, m):{
for(a in m.args){
for(p in paramNames){
if(p.name == a.name){
if(a.value != null){
switch (a.value.expr){
case EConst(c):{
switch(c){
case CString(v, _):{
p.value = v;
}
case CFloat(f): {
p.value = Std.parseFloat(f);
}
case CInt(i):{
p.value = Std.parseInt(i);
}
case _: throw "unsupported constant value for default parameter";
}
}
case _:
}
}
}
}
}
}
case _:
}
return macro $v{paramNames};
}
So we can now use it like this
function func(name:String = "Josh", greeting:String = "Hello"){ return '$greeting $name'};
final args = Macros.deflate(func);
trace(args) // output: [{name: 'name', value:'Josh', {name:'greeting', value:'Hello'}]