Send multiple arguments to the compute function in Flutter - dart

I was trying to use the compute function in Flutter.
void _blockPressHandler(int row, int col) async {
// Called when user clicks any block on the sudoku board . row and col are the corresponding row and col values ;
setState(() {
widget.selCol = col;
}
});
bool boardSolvable;
boardSolvable = await compute(SudokuAlgorithm.isBoardInSudoku , widget.board , widget.size) ;
}
isBoardInSudoku is a static method of class SudokuAlgorithm. Its present in another file. Writing the above code , tells me that
error: The argument type '(List<List<int>>, int) → bool' can't be assigned to the parameter type '(List<List<int>>) → bool'. (argument_type_not_assignable at [just_sudoku] lib/sudoku/SudokuPage.dart:161)
How do i fix this? Can it be done without bringing the SudokuAlgorithm class's methods out of its file ? How to send multiple arguments to the compute function ?
static bool isBoardInSudoku(List<List<int>>board , int size ){ } is my isBoardInSudoku function.

Just put the arguments in a Map and pass that instead.
There is no way to pass more than one argument to compute because it is a convenience function to start isolates which also don't allow anything but a single argument.

Use a map. Here is an example:
Map map = Map();
map['val1'] = val1;
map['val2'] = val2;
Future future1 = compute(longOp, map);
Future<double> longOp(map) async {
var val1 = map['val1'];
var val2 = map['val2'];
...
}

In OOP and in general, it is more elegant to create a class for that with fields you need, that gives you more flexibility and less hassle with hardcoded strings or constants for key names.
For example:
boardSolvable = await compute(SudokuAlgorithm.isBoardInSudoku , widget.board , widget.size) ;
replace with
class BoardSize{
final int board;
final int size;
BoardSize(this.board, this.size);
}
...
boardSolvable = await compute(SudokuAlgorithm.isBoardInSudoku, BoardSize(widget.board, widget.size)) ;

Use a Tuple
Here is some example code from my app:
#override
Future logChange(
String recordId, AttributeValue newValue, DateTime dateTime) async {
await compute(
logChangeNoCompute, Tuple2<String, AttributeValue>(recordId, newValue));
}
Future<void> logChangeNoCompute(Tuple2<String, AttributeValue> tuple) async {
_recordsById[tuple.item1]!.setAttributeValue(tuple.item2);
await storage.setItem(AssetsFileName, toJson());
}

You can have a function whose only argument is a Map so that you can pass multiple parameters by passing a Map with properties and values. However, the problem that I'm encountering now is that I cannot pass functions. If the value of a Map's property is a function I get an error when I run the compute function.
This example works(keep in mind that I've imported libraries and that's the reason why some functions and classes definitions aren't in this example)
Future<List<int>> getPotentialKeys({
#required int p,
#required int q,
})async{
return await compute(allKeys,{
"p" : p,
"q" : q,
});
}
List<int> allKeys(Map<String,dynamic> parameters){
AdvancedCipherGen key = AdvancedCipherGen();
List<int> possibleE = key.step1(p: parameters["p"], q: parameters["q"]);
return possibleE;
}
This does not work(same thing with a function as the value of a property thows an error)
Future<List<int>> getPotentialKeys({
#required int p,
#required int q,
#required Function(AdvancedCipherGen key) updateKey,
})async{
return await compute(allKeys,{
"p" : p,
"q" : q,
"updateKey" : updateKey,
});
}
List<int> allKeys(Map<String,dynamic> parameters){
AdvancedCipherGen key = AdvancedCipherGen();
List<int> possibleE = key.step1(p: parameters["p"], q: parameters["q"]);
//TODO: Update the key value through callback
parameters["updateKey"](key);
return possibleE;
}

easily use a Class, you can Also Use Map or List But using class is Better and Cleaner
class MyFunctionInput{
final int first;
final int second;
MyFunctionInput({required this.first,required this.second});
}
change your function like this
doSomething(MyFunctionInput input){
}
and use it like below
compute(doSomething,MyFunctionInput(first: 1, second: 4));

Related

How to pass a function as an optional parameter with a default value in a function in dart?

This is what I want to implement:
void fun({
bool Function(int i) predicate = (i) => false,
}) {
// do something with 'predicate(something)'
}
But I am getting the error:
The default value of an optional parameter must be constant.dart(non_constant_default_value).
I was able to get arround this error with the following:
bool falsePredicate(int i) => false;
void fun({
bool Function(int i) predicate = falsePredicate,
}) {
// do something with 'predicate(something)'
}
But now the question becomes, why can't I directly create a default function value as in the first set of code? There seems to be no difference between the first and the second cases. How is the function given in the first approach not constant?
As #Noah has pointed to the git discussion, the dart language has this missing piece of compile-time constant functions, which eventually leads to this problem.
Check this post: https://github.com/dart-lang/language/issues/1048
As the post shows, the issue has been raised in mid-2012 and now it's been 8+ years. So the hopes of this being available in the near feature is very less.
However few alternative solutions till then:
Option 1 (separate method):
class ServiceProvider {
static bool falsePredicate(int i) => false;
void fun({
bool Function(int i) predicate = falsePredicate,
}) {
// do something with 'predicate(something)'
}
}
Option 2 (Null checking while using the predicate)
class ServiceProvider {
void fun({
bool Function(int i)? predicate,
}) {
int val = 55; // for demonstration
predicate?.call(val); // Call only if the predicate is non-null
}
}
Option 3 (Only for class constructors)
class ServiceProvider {
final bool Function(int i) _predicate;
ServiceProvider ({bool Function(int i)? predicate})
: _predicate = predicate ?? ((i) => false);
void fun() {
int val = 55;
_predicate(5); // No null check is needed. The predicate is always non-null
}
}

How to set a default value for an optional positional parameter of type Function?

I am passing a Function as an optional parameter to the constructor but I can't assign a default value.
void main() {
Person p = Person();
print(p.foo('Hello'));
}
class Person {
final String Function(String) foo;
Person({this.foo});
}
now trying to assign a default value: Person({this.foo = (val) {return val;});
produces the error: Error: Not a constant expression. I am aware the parameter must be const but using const or even static infront of (val) {return val;} does not work.
Does anyone have an idea how to solve this problem?
You can try this:
void main() {
Person pLower = Person(foo: (a) => a.toLowerCase());
print(pLower.foo('Hello'));
Person pDefault = Person();
print(pDefault.foo('Hello'));
}
class Person {
static String defaultFoo(String a) => a.toUpperCase();
final String Function(String) foo;
Person({this.foo = defaultFoo});
}
Output
hello
HELLO
You can only use constant values (aka. compile-time constants) as default values.
You cannot create a constant function literal, so there is no way to write the function in-line in the constructor.
However, references to top-level or static functions are constants, so you can declare the default value function as a static function or top-level function.
void main() {
Person p = Person();
print(p.foo('Hello')); // Prints "Hello"
}
class Person {
final String Function(String) foo;
Person({this.foo = _identity});
static String _identity(String value) => value;
}
// or as top-level.
// String _identity(String value) => value;
You can (and should) choose to make the function public if the default value is on an instance method, and you expect anyone to extend or implement your class. In that case, they need to declare the same default value.
Another option, which is often at least as useful, is to not use a default value, but replace a null before using the value:
class Person {
final String Function(String) foo;
Person({String Function(String) foo}) : foo = foo ?? _identity;
static String _identity(String value) => value;
}
or even using a non-constant value:
class Person {
final String Function(String) foo;
Person({String Function(String) foo}) : foo = (foo ?? (String x) => x);
}
For a constructor, it makes very little difference. If it was an instance method instead, using ?? to replace null avoids subclasses having to use the exact same function as default value.
Personally I recommend always using ?? instead of a default value. It's more flexible since it allows non-constant values. For non-function default values, you'll have to document the default behavior instead of just letting the dartDoc show {int x = 42}, but for functions, you'll have to document them anyway.

Implement opApply with nogc and inferred parameters

Note: I initially posted an over-simplified version of my problem. A more
accurate description follows:
I have the following struct:
struct Thing(T) {
T[3] values;
int opApply(scope int delegate(size_t, ref T) dg) {
int res = 0;
foreach(idx, ref val; values) {
res = dg(idx, val);
if (res) break;
}
return res;
}
}
Foreach can be used like so:
unittest {
Thing!(size_t[]) thing;
foreach(i, ref val ; thing) val ~= i;
}
However, it is not #nogc friendly:
#nogc unittest {
Thing!size_t thing;
foreach(i, ref val ; thing) val = i;
}
If I change the signature to
int opApply(scope int delegate(size_t, ref T) #nogc dg) { ... }
It works for the #nogc case, but fails to compile for non-#nogc cases.
The solutions I have tried are:
Cast the delegate
int opApply(scope int delegate(size_t, ref T) dg) {
auto callme = cast(int delegate(size_t, ref T) #nogc) dg;
// use callme instead of dg to support nogc
This seems wrong as I am willfully casting a #nogc attribute even onto
functions that do may not support it.
Use opSlice instead of opApply:
I'm not sure how to return an (index, ref value) tuple from my range. Even if
I could, I think it would have to contain a pointer to my static array, which
could have a shorter lifetime than the returned range.
Use a templated opApply:
All attempts to work with this have failed to automatically determine the
foreach argument types. For example, I needed to specify:
foreach(size_t idx, ref int value ; thing)
Which I see as a significant hindrance to the API.
Sorry for underspecifying my problem before. For total transparency,
Enumap is the "real-world" example. It
currently uses opSlice, which does not support ref access to values. My
attempts to support 'foreach with ref' while maintaining #nogc support is what
prompted this question.
Instead of overloading the opApplyoperator you can implement an input range for your type. Input ranges work automatically as the agregate argument in foreach statements:
struct Thing(K,V) {
import std.typecons;
#nogc bool empty(){return true;}
#nogc auto front(){return tuple(K.init, V.init);}
#nogc void popFront(){}
}
unittest {
Thing!(int, int) w;
foreach(val ; w) {
int[] i = [1,2,3]; // spurious allocation
}
}
#nogc unittest {
Thing!(int, int) w;
foreach(idx, val ; w) { assert(idx == val); }
}
This solves the problem caused by the allocation of the delegate used in foreach.
Note that the example is shitty (the range doesn't work at all, and usually ranges are provided via opSlice, etc) but you should get the idea.

How to set values of global variables used in function parameters

I can conveniently change opsCount variable directly from inside the function,
because there is only one of that type of variable.
int opsCount = 0;
int jobXCount = 0;
int jobYCount = 0;
int jobZCount = 0;
void doStats(var jobCount) {
opsCount++;
jobCount++;
}
main() {
doStats(jobXCount);
}
But there are many jobCount variables, so how can I change effectively that variable, which is used in parameter, when function is called?
I think I know what you are asking. Unfortunately, the answer is "you can't do this unless you are willing to wrap your integers". Numbers are immutable objects, you can't change their value. Even though Dart's numbers are objects, and they are passed by reference, their intrinsic value can't be changed.
See also Is there a way to pass a primitive parameter by reference in Dart?
You can wrap the variables, then you can pass them as reference:
class IntRef {
IntRef(this.val);
int val;
#override
String toString() => val.toString();
}
IntRef opsCount = new IntRef(0);
IntRef jobXCount = new IntRef(0);
IntRef jobYCount = new IntRef(0);
IntRef jobZCount = new IntRef(0);
void doStats(var jobCount) {
opsCount.val++;
jobCount.val++;
}
main() {
doStats(jobXCount);
print('opsCount: $opsCount; jobXCount: $jobXCount; jobYCount: $jobYCount; jobZCount: $jobZCount');
}
EDIT
According to Roberts comment ..
With a custom operator this would look like:
class IntRef {
IntRef(this.val);
int val;
#override
String toString() => val.toString();
operator +(int other) {
val += other;
return this;
}
}
void doStats(var jobCount) {
opsCount++;
jobCount++;
}

Assign function/method to variable in Dart

Does Dart support the concept of variable functions/methods? So to call a method by its name stored in a variable.
For example in PHP this can be done not only for methods:
// With functions...
function foo()
{
echo 'Running foo...';
}
$function = 'foo';
$function();
// With classes...
public static function factory($view)
{
$class = 'View_' . ucfirst($view);
return new $class();
}
I did not found it in the language tour or API. Are others ways to do something like this?
To store the name of a function in variable and call it later you will have to wait until reflection arrives in Dart (or get creative with noSuchMethod). You can however store functions directly in variables like in JavaScript
main() {
var f = (String s) => print(s);
f("hello world");
}
and even inline them, which come in handy if you are doing recusion:
main() {
g(int i) {
if(i > 0) {
print("$i is larger than zero");
g(i-1);
} else {
print("zero or negative");
}
}
g(10);
}
The functions stored can then be passed around to other functions
main() {
var function;
function = (String s) => print(s);
doWork(function);
}
doWork(f(String s)) {
f("hello world");
}
I may not be the best explainer but you may consider this example to have a wider scope of the assigning functions to a variable and also using a closure function as a parameter of a function.
void main() {
// a closure function assigned to a variable.
var fun = (int) => (int * 2);
// a variable which is assigned with the function which is written below
var newFuncResult = newFunc(9, fun);
print(x); // Output: 27
}
//Below is a function with two parameter (1st one as int) (2nd as a closure function)
int newFunc(int a, fun) {
int x = a;
int y = fun(x);
return x + y;
}

Resources