Other Quiz: Elegant evaluations...

Erel

B4X founder
Staff member
Licensed User
Longtime User
We need to implement a sub that calls several other subs in a specific order. Each of these subs returns True if the sub was executed successfully and false otherwise.

If any sub returned false then we should stop calling other subs and return false. If all subs returned true then we need to return true.
B4X:
Sub DoSeveralTasks As Boolean
'call TaskA. If good continue to TaskB and so on...
End Sub

Sub TaskA(...) As Boolean

End Sub

Sub TaskB(...) As Boolean

End Sub
'more subs here. Each with a different name and a different number of parameters.

Please use the Spoiler tag in your answer:

SS-2014-10-13_11.51.00.png


It is of course very simple to implement this. The challenge is to write an elegant solution.
 
Last edited:

thedesolatesoul

Expert
Licensed User
Longtime User
B4X:
'TaskList = List of Task subs
Sub DoSeveralTasks as Boolean
   For each TaskSub as String in TaskList
      If CallSub(Me, TaskSub) = False Then
         'Personally I would do some clean up here using DoCleanup(LastTaskRun, TaskList)
         Return False
      End If
   Next
   Return True
End Sub

What would be even more interesting is if someone can implement this with CallSubDelayed!
 
Last edited:
Upvote 0

Troberg

Well-Known Member
Licensed User
Longtime User
Simple:

B4X:
Sub DoSeveralTasks As Boolean
  Return TaskA(...) And TaskB(...) And TaskC(...) and so on...
End Sub

Short circuit evaluation means that as soon as one of the tasks return False, evaluations will stop, and False will be returned.
 
Upvote 0

James Chamblin

Active Member
Licensed User
Longtime User
B4X:
Sub Activity_Resume
    Log(DoSeveralTasks) 'Call and log the result of DoSeveralTasks

End Sub


Sub DoSeveralTasks As Boolean
    'boolean operations like this will 'short circuit' when any function returns false
    'i.e. if TaskB returns false, then TaskC will not be tested, and therefore, not called
    Return (TaskA AND TaskB AND TaskC)
End Sub

Sub TaskA As Boolean
    Log("TaskA")
    Return True
End Sub

Sub TaskB As Boolean
    Log("TaskB")
    Return False
End Sub

Sub TaskC As Boolean
    Log("TaskC")
    Return True
End Sub
 
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
I don't think so.

Don't tell no one but the answer that I was thinking of is a bit less elegant :oops: (but might be more readable in a real project):
B4X:
Sub DoSeveralTasks As Boolean
 Dim success As Boolean = True
 success = success AND TaskA(...)
 success = success AND TaskB(...)
 success = success AND TaskC(...)
 ...
 Return success
End Sub
 
Upvote 0

James Chamblin

Active Member
Licensed User
Longtime User
TheDesolateSoul's answer does allow flexibility in the number of Subs that can be called; however, it doesn't meet the requirement of
'more subs here. Each with a different name and a different number of parameters.

Maybe with the reflection library can an arbitrary number of parameters be called.

using JavaObject library, this can be overcome
B4X:
Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.
    Type Task (S As String, a() As Object)
    Dim TaskList As List

End Sub

Sub Globals
    'These global variables will be redeclared each time the activity is created.
    'These variables can only be accessed from this module.

End Sub

Sub Activity_Create(FirstTime As Boolean)
    'Do not forget to load the layout file created with the visual designer. For example:
    'Activity.LoadLayout("Layout1")
    TaskList.Initialize
    Dim T As Task
    T.S = "TaskA"
    T.a = Array As Object(1)
    TaskList.Add(T)
    Dim T As Task
    T.S = "TaskB"
    T.a = Array As Object(2,"MySelf")
    TaskList.Add(T)
    Dim T As Task
    T.S = "TaskC"
    T.a = Array As Object(1.5,1,"YourSelf")
    TaskList.Add(T)

End Sub

Sub Activity_Resume
    Log(DoSeveralTasks)

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

Sub DoSeveralTasks As Boolean
    Dim jo As JavaObject = Me
   
    For Each T As Task In TaskList
        Dim result As Boolean = jo.RunMethod("_"&T.S.ToLowerCase,T.a)
        If result = False Then Return False
    Next
    Return True
       
   
End Sub

Sub TaskA(n As Int) As Boolean
    Log("TaskA")
    If n = 1 Then Return True
    Return False
End Sub

Sub TaskB(n As Int, s As String) As Boolean
    Log("TaskB")
    If n = 1 AND s = "MySelf" Then Return True
    Return False
   
End Sub

Sub TaskC(d As Double, n As Int, s As String) As Boolean
    Log("TaskC")
    If d > 1.0 AND n = 1 AND s = "YourSelf" Then Return True
    Return False
End Sub
 
Upvote 0

sorex

Expert
Licensed User
Longtime User
aren't most of the above wrong? they still execute the subs even when a pevious one failed.
 
Upvote 0

James Chamblin

Active Member
Licensed User
Longtime User
aren't most of the above wrong? they still execute the subs even when a pevious one failed.
No, they all work correctly.
When you compare several results with AND, the comparisons stop the moment one result is False. So if you have TaskA AND TaskB, TaskA will be called first. If it returns true, then TaskB will be called. If Task A returns false, then the comparison will end and task b well never be called. Something similar happens with OR, except the comparisons end when one returns True.
 
Upvote 0

sorex

Expert
Licensed User
Longtime User
interesting, I thought it would kept on going to evaluate the entire "command"
 
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
It is called short circuit evaluation. It is very useful.

For example you want to check the value of the first element of an array but it is also possible that the array is empty:
B4X:
If arr.Length > 0 AND arr(0) = "some value" Then ...
Without short-circuit evaluation an exception will be raised if the array is empty.
 
Upvote 0

Troberg

Well-Known Member
Licensed User
Longtime User
It is called short circuit evaluation. It is very useful.

For example you want to check the value of the first element of an array but it is also possible that the array is empty:
B4X:
If arr.Length > 0 AND arr(0) = "some value" Then ...
Without short-circuit evaluation an exception will be raised if the array is empty.

Nice, I've never thought of that use of short-circuiting. The same can probably be used to test for non-null, then something else. That's something I'll use!
 
Upvote 0

LucaMs

Expert
Licensed User
Longtime User
TheDesolateSoul's answer does allow flexibility in the number of Subs that can be called; however, it doesn't meet the requirement of


Maybe with the reflection library can an arbitrary number of parameters be called.

using JavaObject library, this can be overcome
B4X:
Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.
    Type Task (S As String, a() As Object)
    Dim TaskList As List

End Sub

Sub Globals
    'These global variables will be redeclared each time the activity is created.
    'These variables can only be accessed from this module.

End Sub

Sub Activity_Create(FirstTime As Boolean)
    'Do not forget to load the layout file created with the visual designer. For example:
    'Activity.LoadLayout("Layout1")
    TaskList.Initialize
    Dim T As Task
    T.S = "TaskA"
    T.a = Array As Object(1)
    TaskList.Add(T)
    Dim T As Task
    T.S = "TaskB"
    T.a = Array As Object(2,"MySelf")
    TaskList.Add(T)
    Dim T As Task
    T.S = "TaskC"
    T.a = Array As Object(1.5,1,"YourSelf")
    TaskList.Add(T)

End Sub

Sub Activity_Resume
    Log(DoSeveralTasks)

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

Sub DoSeveralTasks As Boolean
    Dim jo As JavaObject = Me
  
    For Each T As Task In TaskList
        Dim result As Boolean = jo.RunMethod("_"&T.S.ToLowerCase,T.a)
        If result = False Then Return False
    Next
    Return True
      
  
End Sub

Sub TaskA(n As Int) As Boolean
    Log("TaskA")
    If n = 1 Then Return True
    Return False
End Sub

Sub TaskB(n As Int, s As String) As Boolean
    Log("TaskB")
    If n = 1 AND s = "MySelf" Then Return True
    Return False
  
End Sub

Sub TaskC(d As Double, n As Int, s As String) As Boolean
    Log("TaskC")
    If d > 1.0 AND n = 1 AND s = "YourSelf" Then Return True
    Return False
End Sub


I do not like it, even if it generalizes.

It has two flaws:
requires reflection, then it is as if it does not use the basic language;
it is difficult to read.

I think # 3 is better and simpler
 
Upvote 0

James Chamblin

Active Member
Licensed User
Longtime User
I do not like it, even if it generalizes.

It has two flaws:
requires reflection, then it is as if it does not use the basic language;
it is difficult to read.

I think # 3 is better and simpler
They are 2 different solutions for 2 different circumstances. #3 is great if you know the Subs which need to be called ahead of time.#2 is great for when you don't know and don't need to pass parameters. If neither is known ahead of time, then#7 will work.
 
Upvote 0

James Chamblin

Active Member
Licensed User
Longtime User
Yes, in my example I know.but what if I'm writing a class module or library where the Subs could be different depending on use? Then I wouldn't know ahead of time and a solution like#2 and #7 could come in handy.
 
Upvote 0
Top