I have the following range:
struct Range {
uint data;
#property{
bool empty() { return false; }
uint front() { return data; }
void popFront() { data = data * 2 + 1; }
}
}
Trying to use it,
foreach(c; Rnage()){ /*...*/ } works, but with foreach(i, c; Range()){ /*...*/ } I get:
Error: cannot infer argument types
I need the i just like in something like foreach(i, v; [1,2,3,4]){ }.
Ranges do not support the syntax
foreach(i, c; range)
While it seems obvious that that should work in the simple case, what that index should even be depends on the type of range and doesn't always make sense. So, no counter for the index is provided automatically by foreach, and a range has no way of providing one.
However, thanks to tuple unpacking with foreach, you can do it by using std.range.sequence std.range.zip with your range:
foreach (i, e; zip(sequence!"n"(), range))
{
}
By the way, you shouldn't mark popFront with #property. It doesn't make any sense. popFront takes no arguments and returns no value. It does not act like a variable at all. And the point of properties is to have functions which act like variables. If/when -property's implementation is fully sorted out and it becomes the normal behavior (it's rather buggy at the moment, which is part of why it's a separate switch for the moment), popFront would not be usable as you defined it.
If you use opApply to implement the range, You can overload one version for the without-index style and for the with-index style:
struct Range {
int opApply(int delegate(int) action){
uint data=0;
while(true){
action(data);
data=data*2+1;
}
return 0;
}
int opApply(int delegate(uint,int) action){
uint i=0;
foreach(element;this){
action(i++,element);
}
return 0;
}
}
Related
I'm working through an exercise from this course. This code:
void main() {
const order = ['pepperoni', 'margherita', 'pineapple'];
print("Total: ${calculateTotal(order)}");
}
double calculateTotal(List<String> order) {
var total = 0.0;
const pizzaPrices = {
'margherita': 5.5,
'pepperoni': 7.5,
'vegetarian': 6.5,
};
for (var item in order) {
if (pizzaPrices[item]!=null) {
total += pizzaPrices[item];
}
}
return total;
}
Produces the error message The argument type 'double?' can't be assigned to the parameter type 'num'. pointing to the line total += pizzaPrices[item];
total += pizzaPrices[item]! compiles as expected, without errors.
I don't understand why the compiler would need the !, since it already knows pizzaPrices[item] cannot be null.
The reason is that the [] operator on Map is defined to return a nullable type since if the element you search for are not in the map, the [] operator will return null.
It might look obvious to you, but the compiler cannot know for sure that just because you checked the returned value from pizzaPrices[item] once, it will return the same value again the second time you ask (e.g. in some custom made Map implementation).
A solution is instead to save the value in a local variable which you can then check for null. Dart will in this case promote the variable as expected:
void main() {
const order = ['pepperoni', 'margherita', 'pineapple'];
print("Total: ${calculateTotal(order)}");
}
double calculateTotal(List<String> order) {
var total = 0.0;
const pizzaPrices = {
'margherita': 5.5,
'pepperoni': 7.5,
'vegetarian': 6.5,
};
for (var item in order) {
final pizzaPrice = pizzaPrices[item];
if (pizzaPrice != null) {
total += pizzaPrice;
}
}
return total;
}
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.
Consider the following code which prints out the even numbers up to 20:
import std.stdio;
class count_to_ten{
static int opApply()(int delegate(ref int) dg) {
int i = 1;
int ret;
while(i <= 10){
ret = dg(i);
if(ret != 0) {
break;
}
i++;
}
return ret;
}
}
void main() {
int y = 2;
foreach(int x; count_to_ten) {
writeln(x * y);
}
}
The syntax of opApply requires that it take a delegate or function as a normal argument. However, even if we relaxed that and allowed opApply to take a function as a template argument, we still would have no recourse for delegates because D doesn't provide any way to separate the stack-frame pointer from the function pointer. However, this seems like it should be possible since the function-pointer part of the delegate is commonly a compile-time constant. And if we could do that and the body of the loop was short, then it could actually be inlined which might speed this code up quite a bit.
Is there any way to do this? Does the D compiler have some trick by which it happens automagically?
I'm using the style class below to mimick enums (from Does Dart support enumerations?)
It is working fine in that this snippet produces expected results.
void main() {
InterpolationType it = InterpolationType.LINEAR;
print("it is $it and stringified ${stringify(it)}");
print(InterpolationType.fromJson(it.toJson()));
}
But the DartEditor is complaining about "Expected constant expression" in the case statements of fromJson method. Is there a const I can throw in somewhere to get rid of this complaint?
class InterpolationType {
static const LINEAR = const InterpolationType._(0);
static const STEP = const InterpolationType._(1);
static const CUBIC = const InterpolationType._(2);
static get values => [
LINEAR,
STEP,
CUBIC
];
final int value;
const InterpolationType._(this.value);
String toString() {
switch(this) {
case LINEAR: return "LINEAR";
case STEP: return "STEP";
case CUBIC: return "CUBIC";
}
}
int toJson() {
return this.value;
}
static InterpolationType fromJson(int v) {
switch(v) {
case LINEAR.value: return LINEAR;
case STEP.value: return STEP;
case CUBIC.value: return CUBIC;
}
}
static InterpolationType fromString(String s) {
switch(s) {
case "LINEAR": return LINEAR;
case "STEP": return STEP;
case "CUBIC": return CUBIC;
}
}
}
As you discovered: accessing fields from a const object is not a constant operation. So the editor (as well as the VM and dart2js) are right.
With the current syntax there is no way to express a (informal) contract that a field of a class will always be a final field. For example, I could change the value-field to be a getter instead of a field. The interface-contract of the class definitely allows me to do that, because I never told anybody that I would keep "value" as a field. However if I did that it would break every program that relied on the existence of this final field.
As a consequence the current behavior is very unlikely to change.
However: in theory it would be possible to improve the Dart language so that you could use "const" instead of "final" for local fields, and initialize them with initializer lists. And in this case accessing the field could be considered a constant operation. I currently don't see any downsides to this behavior and it would be backwards-compatible.
// WARNING: What follows DOES NOT WORK, just a potential example
class InterpolationType {
const value; // Note the "const" instead of "final".
const InterpolationType._(this.value);
}
The language is already pretty stable but you can open a bug at http://dartbug.com/ and suggest this behavior. It's not very likely that the feature-request would be accepted, but it's definitely worth a try.
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;
}