I'm adding coroutine support to an interpreter I'm writing and I'd like to do something like the following:
typedef enum {
bar_stuff,
bar_other
} Bar;
typedef enum {
foo_error=-1,
foo_none=0,
foo_again
} Foo_state;
Foo_state do_foo(Bar bar,Foo_state foo)
{
switch(foo)
{
case foo_none: //start
switch(bar)
{
case bar_stuff:
//do stuff
return foo_none;
case bar_other:
//do other stuff
return foo_again;
case foo_again: //!! this doesn't work
/* edit: this is supposed to be a case of
* switch(foo), not switch(bar)
*/
//do more other stuff
return foo_none;
default:
//stuff
return foo_none;
}
default:
//fail
return foo_error;
}
}
Obviously this dosn't work (I get duplicate case value, alternative is probably undefined behavior/segfault). I could write switch(bar) as an if/else if/else chain, but I was hoping there was a better way.
I'm using gcc if that makes a difference.
Edit:
The following would work, but would be a PITA to maintain:
Foo_state do_foo2(Bar bar,Foo_state foo)
{
switch(foo)
{
case foo_none: goto case_foo_none;
case foo_again: goto case_foo_again;
default:
//fail
return foo_error;
}
case_foo_none: //start
switch(bar)
{
case bar_stuff:
//do stuff
return foo_none;
case bar_other:
//do other stuff
return foo_again;
case_foo_again:
//do more other stuff
return foo_none;
default:
//stuff
return foo_none;
}
}
Edit 2:
Well, this doesn't seem to be yielding the aforementioned 'better way', so I'd like know if anyone foresees a problem with writing it like this:
Foo_state do_foo3(Bar bar,Foo_state foo)
{
switch(foo)
{
case foo_none: //start
if(bar == bar_stuff)
{
printf("do stuff\n");
return foo_none;
}
else if(bar == bar_other)
{
printf("do other stuff\n");
return foo_again;
case foo_again: //continue
printf("do more other stuff\n");
return foo_none;
}
else
{
printf("stuff\n");
return foo_none;
}
default:
//fail
return foo_error;
}
}
The problem I see with this is missing a bar_* value (since there are several functions like this, and some of the enums have dozens of values), but I suppose a test script for that should work...
You can also just put { } inside each case: statement
without them the whole case stack is evaluated as a single unit, so no variables can be defined within one case:
But by putting
case blah:
{
// do stuff
}
break;
You can put anythign you want inside the case statement.
Hm ... use an equivalent of a Karnaugh Map to simplify the logic and do it all with
if (cond1 && cond2) {
doX();
return;
}
if (cond3 && cond4) {
doY();
return;
}
// Sometimes you can take shortcuts
if (cond5) {
doZ();
} else {
doW();
}
return;
This code is readable. Nested things are best avoided if possible.
Checking the simplest conditions first will make the function simpler.
IN YOUR CASE START WITH:
Foo_state do_foo2(Bar bar,Foo_state foo) {
if (foo != foo_none && foo != foo_again) {
return foo_error;
}
...
if (foo == foo_none) {
...
}
// Implicit Else
...
A simple fix would be to change the values in the bar_ enum so that they are unique with respect to the foo_ enum. That doesn't address the fact, however, that your code is confusing. Why would you look for a foo_ value inside of the bar_ switch statement? Syntactically, it is valid (as long as the values are unique) but its poor coding.
Ok, it seems that your code does this:
bar \ foo foo_none foo_again other
bar_stuff doStuff, return foo_none do more other stuff, return foo_none return foo_error
bar_other do other stuff, return foo_again do more other stuff, return foo_none return foo_error
other stuff, return foo_none do more other stuff, return foo_none return foo_error
This is what I meant by a Karnaugh Map. Now, here is the simplest implementation:
Foo_state do_foo2(Bar bar,Foo_state foo) {
if (foo == foo_again) {
// do more stuff
return foo_none;
}
if (foo != foo_none) { // other
return foo_error;
}
// foo_none
if (bar == bar_stuff) {
// do stuff
return foo_none;
}
if (bar == bar_other) {
// do other stuff
return foo_again;
}
// At this point bar = other
// stuff
return foo_none;
}
I believe this does the same thing as your code, but does not use switches and gotos. You can fill out a table with the result, and you can also put both implementations through a unit tests to make sure they do the same thing for all inputs.
Related
I have a column with editable cells. The cell displays an operator like ">" "<" or "=". But the data model represents this with 0, 1, 2 and so on.
So I need a way to convert the data from it's numeric representation to it's operator counterpart when displayed, and then I need to convert it back when saving the data.
After looking around a little bit it seems like using a get/set function with the ng-model is the way to do it. But I can't get it right. There are very few examples of this online.
My cell template looks like this:
<div class="ui-grid-cell-contents">
<input type="text" class="sample form-control" ng-model="row.entity.operator" ng-model-getter-setter="get=grid.appScope.listCtrl.getOperator(row.entity.operator);set=grid.appScope.listCtrl.setOperator(row.entity.operator)">
</div>
But it doesn't do anything. The cell just displays the ng-model value. As I said I couldn't even find a good example online where they use get and set functions, so any help would be much appreciated!
I'll include the get and set function as well:
public getOperator(operator: string): string {
switch (operator) {
case "0": {
return "";
}
case "1": {
return ">";
}
case "2": {
return "<";
}
case "3": {
return "=";
}
default: {
return "";
}
}
}
public setOperator(operator: string): string {
switch (operator) {
case "": {
return "0";
}
case ">": {
return "1";
}
case "<": {
return "2";
}
case "=": {
return "3";
}
default: {
return "0";
}
}
}
I'm converting a JavaScript library to Haxe.
It seems Haxe is very similar to JS, but in working I got a problem for function overwriting.
For example, in the following function param can be an an integer or an array.
JavaScript:
function testFn(param) {
if (param.constructor.name == 'Array') {
console.log('param is Array');
// to do something for Array value
} else if (typeof param === 'number') {
console.log('param is Integer');
// to do something for Integer value
} else {
console.log('unknown type');
}
}
Haxe:
function testFn(param: Dynamic) {
if (Type.typeof(param) == 'Array') { // need the checking here
trace('param is Array');
// to do something for Array value
} else if (Type.typeof(param) == TInt) {
trace('param is Integer');
// to do something for Integer value
} else {
console.log('unknown type');
}
}
Of course Haxe supports Type.typeof() but there isn't any ValueType for Array. How can I solve this problem?
In Haxe, you'd usually use the is operator for this instead of Type.typeof(). Behind the scenes, the operator uses Std.isOfType().
if (param is Array) {
trace('param is Array');
} else if (param is Int) {
trace('param is Integer');
} else {
trace('unknown type');
}
It's possible to use Type.typeof() as well, but less common - you can use pattern matching for this purpose. Arrays are of ValueType.TClass, which has a c:Class<Dynamic> parameter:
switch (Type.typeof(param)) {
case TClass(Array):
trace("param is Array");
case TInt:
trace("param is Int");
case _:
trace("unknown type");
}
I'm pretty sure I'm doing this 100% wrong, but I have three buttons that toggle separate functions. I'm trying to determine the best way to write the functions in my controller.
The buttons:
<button ng-class="{selected:vm.selectedOption==='1'}" ng-click="vm.selectOption('1')">1</button>
<button ng-class="{selected:vm.selectedOption==='2'}" ng-click="vm.selectOption('2')">2</button>
<button ng-class="{selected:vm.selectedOption==='3'}" ng-click="vm.selectOption('3')">3</button>
My methods:
vm.selectOption = function(1) {
// function
};
vm.selectOption = function(2) {
// function
};
vm.selectOption = function(3) {
// function
};
or
vm.selectOption = function(option) {
if (vm.selectedOption==='1') {
// function
};
if (vm.selectedOption==='2') {
// function
};
if (vm.selectedOption==='3') {
// function
};
};
or I imagine there is a more correct way to do this that I'm not seeing.
You are passing the option to select to the function so:
vm.selectOption = function(option) {
vm.selectedOption = option;
}
If you are looking to do something specific based on the option you just set:
vm.selectOption = function(option) {
vm.selectedOption = option;
switch(option) {
case '1':
// do something
break;
case '2':
// do something else
break;
case '3':
// do something completely different
break;
}
}
I'm using $filter to iterate through an array and fetch a specific value
Below is my code:
var selected = $filter('filter')($scope.folders, {url: el.selected[0] });
This code is working, but I got a problem when the url contain an accent and space like so :
/Users/Me/project/products/Poste à souder
In that case the string comparaison isn't working anymore.
What is the cleaner way to solve this situation ?
That true. As a francophone, I've often encounter encoding/decoding issues with angularjs.
The source code of the default filter is as follow
function filterFilter()
{
return function(array, expression, comparator)
{
if (!isArrayLike(array))
{
if (array == null)
{
return array;
}
else
{
throw minErr('filter')('notarray', 'Expected array but received: {0}', array);
}
}
var expressionType = getTypeForFilter(expression);
var predicateFn;
var matchAgainstAnyProp;
switch (expressionType)
{
case 'function':
predicateFn = expression;
break;
case 'boolean':
case 'null':
case 'number':
case 'string':
matchAgainstAnyProp = true;
//jshint -W086
case 'object':
//jshint +W086
predicateFn = createPredicateFn(expression, comparator, matchAgainstAnyProp);
break;
default:
return array;
}
return Array.prototype.filter.call(array, predicateFn);
};
}
and the predicate generator stand as follow: it generate the default comparator if the provided one is not a function
function createPredicateFn(expression, comparator, matchAgainstAnyProp)
{
var shouldMatchPrimitives = isObject(expression) && ('$' in expression);
var predicateFn;
if (comparator === true)
{
comparator = equals;
}
else if (!isFunction(comparator))
{
comparator = function(actual, expected)
{
if (isUndefined(actual))
{
// No substring matching against `undefined`
return false;
}
if ((actual === null) || (expected === null))
{
// No substring matching against `null`; only match against `null`
return actual === expected;
}
if (isObject(expected) || (isObject(actual) && !hasCustomToString(actual)))
{
// Should not compare primitives against objects, unless they have custom `toString` method
return false;
}
actual = lowercase('' + actual);
expected = lowercase('' + expected);
return actual.indexOf(expected) !== -1;
};
}
predicateFn = function(item)
{
if (shouldMatchPrimitives && !isObject(item))
{
return deepCompare(item, expression.$, comparator, false);
}
return deepCompare(item, expression, comparator, matchAgainstAnyProp);
};
return predicateFn;
}
Too much speech. You have the choice:
Provide a comparator to your filter see the doc
but remember that you can't define inline function in angular template
you can define a function in that scope, but it will only be available in that scope
You can write your own filter
.filter('myCustomFilter', function()
{
return function(input, criteria)
{
... // your logic here
return ...// the filtered values
};
})
Maybe it's best to write your own filter:
app.filter("customFilter", function () {
//the filter will accept an input array, the key you want to look for and the value that the key should have
return function (array, key, value) {
return array.filter(function(x){
return (x.hasOwnProperty(key) && (x[key] === value));
});
};
});
And use it in your controller like:
$scope.filtered = $filter("customFilter")($scope.folders, "url", "/Users/Me/project/products/Poste à souder");
Check out a working demo here.
I'm basically trying to create a macro that automatically generates an if/else if chain by supplying one common outcome statement for all conditions.
This is what I've tried so far (modified the code just to show as an example):
import haxe.macro.Expr;
class LazyUtils {
public macro static function tryUntilFalse( xBool:Expr, xConds:Array<Expr> ) {
var con1, con2, con3, con4, con5;
/*
* Here's a switch to handle specific # of conditions, because for-loops
* don't seem to be allowed here (at least in the ways I've tried so far).
*
* If you know how to use for-loop for this, PLEASE do tell!
*/
switch(xConds.length) {
case 1: {
con1 = conds[0];
return macro {
if (!$con1) $xBool;
}
}
case 2: {
con1 = conds[0];
con2 = conds[1];
return macro {
if (!$con1) $xBool;
else if (!$con2) $xBool;
}
}
case 3: {
con1 = conds[0];
con2 = conds[1];
con3 = conds[2];
return macro {
if (!$con1) $xBool;
else if (!$con2) $xBool;
else if (!$con3) $xBool;
}
}
// ... so on and so forth
}
return macro { trace("Unhandled length of conditions :("); };
}
}
Then, in theory it could be used like this:
class Main {
static function main() {
var isOK = true;
LazyUtils.tryUntilFalse( isOK = false, [
doSomething(),
doSomethingElse(), //Returns false, so should stop here.
doFinalThing()
]);
}
static function doSomething():Bool {
// ???
return true;
}
static function doSomethingElse():Bool {
// ???
return false;
}
static function doFinalThing():Bool {
return true;
}
}
Which should generate this condition tree:
if (!doSomething()) isOK = false;
else if (!doSomethingElse()) isOK = false;
else if (!doFinalThing()) isOK = false;
Alternatively, I suppose it could output this instead:
if(!doSomething() || !doSomethingElse() || !doFinalThing()) isOK = false;
Looking back at this now, true - it may not make much sense to write a whole macro to generate code that would be easier to type out in it's raw format.
But for the sake of learning about macros, does anyone know if multiple expressions can be passed in an Array<Expr> like I tried in the above code sample?
You probably couldn't get the xConds argument to behave like you expected because the final argument of an expression macro with the type Array<Expr> is implicitly a rest argument. That means you ended up with an array that contained a single EArrayDecl expression. This can be fixed by simply omitting the [].
Regarding generating the if-else-chain - let's take a look at EIf:
/**
An `if(econd) eif` or `if(econd) eif else eelse` expression.
**/
EIf( econd : Expr, eif : Expr, eelse : Null<Expr> );
The chain can be thought of as a singly linked list - the eelse if the first EIf should reference the next EIf and so forth, until we stop with eelse = null for the last EIf. So we want to generate this for your example (pseudo-code):
EIf(doSomething(), isOk = false, /* else */
EIf(doSomethingElse, isOk = false, /* else */
EIf(doFinalThing(), isOk = false, null)
)
)
Recursion works well for this.
Typically it's more convenient to work with reification than raw expressions like I do here, but I'm not sure the former is really possible when dynamically generating expressions like this.
import haxe.macro.Context;
import haxe.macro.Expr;
class LazyUtils {
public macro static function tryUntilFalse(setBool:Expr, conditions:Array<Expr>):Expr {
return generateIfChain(setBool, conditions);
}
private static function generateIfChain(eif:Expr, conditions:Array<Expr>):Expr {
// get the next condition
var condition = conditions.shift();
if (condition == null) {
return null; // no more conditions
}
// recurse deeper to generate the next if
var nextIf = generateIfChain(eif, conditions);
return {
expr: EIf(condition, eif, nextIf),
pos: Context.currentPos()
};
}
}
And Main.hx (mostly unchanged):
class Main {
static function main() {
var isOK = true;
LazyUtils.tryUntilFalse(isOK = false,
!doSomething(),
!doSomethingElse(), //Returns false, so should stop here.
!doFinalThing()
);
}
static function doSomething():Bool {
trace("doSomething");
return true;
}
static function doSomethingElse():Bool {
trace("doSomethingElse");
return false;
}
static function doFinalThing():Bool {
trace("doFinalThing");
return true;
}
}
To keep things simple I inverted the function call arguments with ! at the call site instead of handling that in the macro.
You can use -D dump=pretty to generate AST dumps and check what code is being generated. Here's the result:
if ((! Main.doSomething()))isOK = false else if ((! Main.doSomethingElse()))isOK = false else if ((! Main.doFinalThing()))isOK = false;