This snippet is of no practical significance. I've been just getting used to the closure idea.
var cls = () {
var x = 5;
return {
'x': x,
'inc': () {x++;},
};
} ();
void main() {
print(cls['x']);
print(cls['inc']);
cls['inc']();
print(cls['x']);
}
DartPad output:
5
Closure '_closure'
5
Error compiling to JavaScript:
main.dart:18:13:
Error: The method 'call' isn't defined for the class 'dart.core::Object'.
The desired output would have been something like
5
6
What'd be the cleanest approach to this kind of exercise?
UPD:
The working example, courtesy of Günter Zöchbauer:
var cls = () {
var x = 5;
var map = <String, dynamic>{
'x': x,
};
map['inc'] = () {map['x']++;};
return map;
} ();
void main() {
print(cls['x']);
print(cls['inc']);
cls['inc']();
print(cls['x']);
}
DartPad output:
5
Closure '_closure'
6
You have to declare the 'x' entry as a Function.
In your code, you set 'x' to the value of the 'x' variable (5) when you return the map. The value will always be 5 and will not update.
var cls = () {
var x = 5;
return {
'x': () => x,
'inc': () {x++;},
};
}();
void main() {
print(cls['x']()); // 5
print(cls['x']); // Closure: () => int
print(cls['inc']); // Closure: () => Null
cls['inc']();
print(cls['x']()); // 6
}
var x = 5;
var cls = () {
return {
'x': x,
'inc': () {x++;},
};
} ();
You would need to move out the variable declaration, otherwise you'd re-declare and re-initialize it to 5 at every call.
update
var cls = () {
var x = 5;
var map = {
'x': x,
// 'inc': () {map['val']++;}, // not possible to reference the `map` variable that is just declared here so we need to move this out
};
map['inc'] = () {map['x']++;};
} ();
Related
The listener function can listen to any parameter type(not only listener type). This has nothing related to widgets (this should work successfully in https://dartpad.dev without using the flutter).
ex.
int a = 0;
listener((a>0)=>print("A = $a"));
a= 1; //A = 1
a= -1; //
a= 2; //A = 2
You can use ValueNotifier for this. It's a ChangeNotifier that is triggered when the value is replaced with something that is not equal to the old value as evaluated by the equality operator ==.
Here is a nice tutorial about this approach.
The basic method is to create the function for updating the parameter that you want to add to the listener.
void test() {
int a = 0;
void updateA(newA) {
if(newA is! int) return;
a = newA;
if (a > 0) print("A = $a");
}
updateA(1);
updateA(-1);
updateA(2);
}
A better way is to create parameters with class.
void main() {
ParameterWithListener a = ParameterWithListener(data: 0);
a.listener = () {
if (a.data is int && a.data > 0) print("A = ${a.data}");
};
a.update(1);
a.update(-1);
a.update(2);
}
class ParameterWithListener {
ParameterWithListener({this.data, this.listener});
dynamic data;
Function()? listener;
Future update(data) async {
this.data = data;
if (listener is Function()) await listener!();
}
}
result:
A = 1
A = 2
I have an app with an arbitrary number of elements that all call a function that needs to take an argument, defined on creation. This is a simplified example but here I'd be hoping to make 3 buttons that print 0,1,2 but just makes 3 buttons that print 3.
var application_window = new Gtk.ApplicationWindow (this);
var grid = new Gtk.Grid ();
for (int i = 0; i < 3; i++) {
var button = new Gtk.Button() {expand=true};
button.clicked.connect (() => {
print(i.to_string());
});
grid.add(button);
}
application_window.add(grid);
application_window.show_all ();
How can I change my app to print 123 instead?
Here is my base code:
public class MyApplication : Gtk.Application {
public MyApplication () {
Object(application_id: "testing.my.application",
flags : ApplicationFlags.FLAGS_NONE);
}
protected override void activate () {
var application_window = new Gtk.ApplicationWindow (this);
var grid = new Gtk.Grid ();
for (int i = 0; i < 3; i++) {
var button = new Gtk.Button() {expand=true};
button.clicked.connect (() => {
print(i.to_string());
});
grid.add(button);
}
application_window.add(grid);
application_window.show_all ();
}
public static int main (string[] args) {
MyApplication app = new MyApplication ();
return app.run (args);
}
}
If you execute it like that, you get 333 as stdout.
The problem is in the capturing code:
for (int i = 0; i < 3; i++) {
var button = new Gtk.Button() {expand=true};
button.clicked.connect (() => {
print(i.to_string());
});
The closure is capturing the variable i by location. That means when you change the i variable after creating the closures the change will be visibile in the closure as well.
Other programming languages (e. g. C++) have explicit capture lists to avoid this problem.
A quick and dirty solution would be using a local variable inside the scope of the loop:
for (int i = 0; i < 3; i++) {
var captured_i = i;
var button = new Gtk.Button() {expand=true};
button.clicked.connect (() => {
print(captured_i.to_string());
});
This prints: 012 as intended.
A better solution would be using a function that returns the closure as a delegate. I just tried that, but for some reason it does not work:
public class MyApplication : Gtk.Application {
public MyApplication () {
Object(application_id: "testing.my.application",
flags : ApplicationFlags.FLAGS_NONE);
}
delegate void ButtonClick();
private ButtonClick make_print_event (int i) {
return () => print (i.to_string());
}
protected override void activate () {
var application_window = new Gtk.ApplicationWindow (this);
var grid = new Gtk.Grid ();
for (int i = 0; i < 3; i++) {
var button = new Gtk.Button() { expand=true };
var print_event = make_print_event (i);
button.clicked.connect (print_event);
grid.add(button);
}
application_window.add (grid);
application_window.show_all ();
}
public static int main (string[] args) {
MyApplication app = new MyApplication ();
return app.run (args);
}
}
The compiler (valac-0.52) warns:
three_buttons.vala:20.37-20.47: warning: copying delegates is not supported
three_buttons.vala:20.37-20.47: warning: Connecting delegates to signals is experimental
button.clicked.connect (print_event);
^^^^^^^^^^^
I know how to print map using foreach() method
var x = {1:'One',2:'Two',3:'Three',4:'Four',5:'Five'};
x.foreach((i,j){
print(i);
print(j);
});
and using normal for loop
other methods to print map ?
You can also directly print the map:
void main() {
var x = {1:'One',2:'Two',3:'Three',4:'Four',5:'Five'};
print(x);
}
The result will be pretty neat:
{1: One, 2: Two, 3: Three, 4: Four, 5: Five}
You will find the implementation of mapToString here.
static String mapToString(Map<Object?, Object?> m) {
// Reuses the list in IterableBase for detecting toString cycles.
if (_isToStringVisiting(m)) {
return '{...}';
}
var result = StringBuffer();
try {
_toStringVisiting.add(m);
result.write('{');
bool first = true;
m.forEach((Object? k, Object? v) {
if (!first) {
result.write(', ');
}
first = false;
result.write(k);
result.write(': ');
result.write(v);
});
result.write('}');
} finally {
assert(identical(_toStringVisiting.last, m));
_toStringVisiting.removeLast();
}
return result.toString();
}
Is there any way to convert an int to a int Function() in Dart?
For example:
a to ()=>a
You mean like this? This function will return a function which returns the int value you provided when the method was created.
int Function() intToIntFunction(int i) => () => i;
Updated
You can also do this if you want the current value of the integer each time the method are called:
void main() {
int a = 5;
int Function() funA = () => a;
print(funA()); // 5
a++;
print(funA()); // 6
}
This is my code:
var badget = function () {
var privetVar = 23;
var privetFunc = function (a) {
return privetVar + a;
}
return {
publicFunc: function (b) {
console.log(privetFunc (b));
}
}
}();
It works well; I have access to the publicFunc() using badget.publicFunc(), which has access to the privetVar and privetFunc() due to "closures".
However, someone told me I must use parentheses like this:
var badget = (function() {
var privetVar = 23;
var privetFunc = function(a) {
return privetVar + a;
}
return {
publicFunc: function(b) {
console.log(privetFunc(b));
}
}
})();
Is this second example considered a preferable syntax?
No, the parentheses are not required in this example. Typically people don't use the return value of an IIFE, so the parentheses are required to differentiate a function expression from a function statement.
Since your function declaration in your first example is already part of an assignment expression, it's already a function expression, so the parentheses aren't required.
TL;DR
Valid
var badget = function () {
...
}();
(function () {
...
})();
(function () {
...
}());
Valid (but not necessary)
var badget = (function () {
...
})();
var badget = (function () {
...
}());
Invalid (function statements cannot be IIFEs)
function () {
...
}();