diff --git a/HISTORY.md b/HISTORY.md
index dcfd1cfa..429fcff1 100755
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -13,6 +13,9 @@
* **Documentation Updates:**
* **Translation Updates:**
+### 2022-02-21
+* threads: optimized hyper-if/else to skip repeated evaluation of literal true/false cases
+
### 2022-02-18
* new MQTT extension and library, thanks, Simon and Xavier!
diff --git a/snap.html b/snap.html
index bd303ad4..00ee2275 100755
--- a/snap.html
+++ b/snap.html
@@ -17,7 +17,7 @@
-
+
diff --git a/src/threads.js b/src/threads.js
index 0fb8e0f5..3136a6ce 100644
--- a/src/threads.js
+++ b/src/threads.js
@@ -64,7 +64,7 @@ SnapExtensions, AlignmentMorph, TextMorph, Cloud, HatBlockMorph*/
/*jshint esversion: 6*/
-modules.threads = '2022-February-16';
+modules.threads = '2022-February-21';
var ThreadManager;
var Process;
@@ -2349,8 +2349,11 @@ Process.prototype.doIfElse = function () {
Process.prototype.reportIfElse = function (block) {
var inputs = this.context.inputs,
+ accumulator,
condition,
- expression;
+ expression,
+ trueIsBlock,
+ falseIsBlock;
if (inputs.length < 1) {
// evaluate the first input, either a Boolean or a (nested) list
@@ -2361,22 +2364,48 @@ Process.prototype.reportIfElse = function (block) {
if (inputs[0] instanceof List && this.enableHyperOps) {
// hyperize a (nested) list of Booleans
if (this.context.accumulator === null) {
- this.context.accumulator = [];
+ // cache literal true/false cases for optimized evaluation
+ trueIsBlock = block.inputs()[1] instanceof BlockMorph;
+ falseIsBlock = block.inputs()[2] instanceof BlockMorph;
+ this.context.accumulator = {
+ results : [],
+ trueIsLiteral : !trueIsBlock,
+ trueCase : trueIsBlock ? null : block.inputs()[1].evaluate(),
+ falseIsLiteral : !falseIsBlock,
+ falseCase : falseIsBlock ? null : block.inputs()[2].evaluate()
+ };
} else if (inputs.length > 1) {
// retrieve & remember previous result & remove it from the inputs
- this.context.accumulator.push(inputs.pop());
+ this.context.accumulator.results.push(inputs.pop());
}
- if (this.context.accumulator.length === inputs[0].length()) {
+ accumulator = this.context.accumulator;
+ if (accumulator.results.length === inputs[0].length()) {
// done with all the conditions in the current list
this.returnValueToParentContext(
- new List(this.context.accumulator)
+ new List(accumulator.results)
);
this.popContext();
return;
}
- condition = inputs[0].at(this.context.accumulator.length + 1);
+ condition = inputs[0].at(accumulator.results.length + 1);
+
+ // optimize literal true-/false- cases for atomic conditions:
+ if (!(condition instanceof List)) {
+ if (condition && accumulator.trueIsLiteral) {
+ accumulator.results.push(accumulator.trueCase);
+ return;
+ }
+ if (!condition && accumulator.falseIsLiteral) {
+ accumulator.results.push(accumulator.falseCase);
+ return;
+ }
+ }
+
this.pushContext(block); // recursive call
- this.context.addInput(condition); //
+ this.context.addInput(condition);
+ // optimize evaluation of literals:
+ this.context.accumulator = copy(accumulator);
+ this.context.accumulator.results = [];
return;
}