How to avoid cyclic dependencies without battling with the compiler in svelte? - javascript

I was reading the doc, and after tweaking its sample code, I managed to get compiler barked at me about cyclic dependencies like this:
<script>
let count = 0;
$: double = count * 2;
$: if (double >= 20) {
alert(`count is dangerously high!`);
count = 9;
}
function handleClick() {
count += 1;
}
</script>
<button on:click={handleClick}>
Clicked {count} {count === 1 ? 'time' : 'times'}
</button>
I asked on discord how to fix it, and people suggested that I should hide the dependencies from the compiler like this:
<script>
let count = 0;
$: double = count * 2;
function resetCount() {
count = 9;
}
$: if (double >= 20) {
alert(`count is dangerously high!`);
resetCount();
}
function handleClick() {
count += 1;
}
</script>
<button on:click={handleClick}>
Clicked {count} {count === 1 ? 'time' : 'times'}
</button>
It works, but I got a couple questions:
Battling with the compiler doesn't sound right to me, is there any other better ways to fix this?
A more general question is that does cyclic dependencies happen quite often to people who have written large amount of svelte code? Is it normal or it usually signals a bad design?
Thanks.

The answer from #morphyish sort-of provides a fix, because as they stated:
a reactive statement can't trigger itself
However, I see that as somewhat of a technicality, and still see the provided solution as conceptually having a cyclic dependency: we still have count -> double -> count -> ....
And because we've bypassed this cyclic dependency warning by merging the statements into a single reactive block, we've actually also introduced a bug:
This bug occurs because the double value is set to 10 * 2 = 20 at the beginning of the reactive block, then count is set to 9 within the if-statement, but then double is not set back to 9 * 2 = 18 because the reactive block doesn't trigger again.
My suggestion in this, and similar cases would be to re-evaluate what your dependencies actually are in order to remove these cycles:
double = count * 2;
^ So double depends on count, that one's easy.
if (double >= 20) {
alert('count is dangerously high!');
count = 9;
}
^ At first glance it might seem like our count-reset logic depends on double, but seeing as we've already established that double depends on count – and this block is ultimately concerned with count – this logic really depends on count not double.
So in my view, the best solution would be to modify the conditional to match the actual dependency:
<script>
let count = 0;
$: double = count * 2;
$: if (count >= 10) {
alert(`count is dangerously high!`);
count = 9;
}
function handleClick() {
count += 1;
}
</script>
<button on:click={handleClick}>
Clicked {count} {count === 1 ? 'time' : 'times'}
</button>

You could fix this issue by organizing your code slightly differently:
<script>
let count = 0;
let double;
$: {
double = count * 2;
if (double >= 20) {
alert(`count is dangerously high!`);
count = 9;
}
}
function handleClick() {
count += 1;
}
</script>
<button on:click={handleClick}>
Clicked {count} {count === 1 ? 'time' : 'times'}
</button>
You can group reactive statements together using {} with a small caveat: Svelte won't automatically write the variable declaration as it would otherwise.
I've never run into this issue before, but in your case it looks like both statement are dependent on count being updated, albeit indirectly for the second one. So it makes sense to actually group them into a single statement.
It also solves your issue as a reactive statement can't trigger itself.
However it means that if you want to also update double you would need to do it explicitly.

Related

After Effects: Javascript - Undefined value used in the expression(Could be out of range array subscript)

I'm not a programmer by any means. I'm an animator trying to use JS expressions in After Effects. I'm getting an "Undefined value used in expression" error on line 1 where I define a variable.I already showed it to my friend on discord who is a cs major, and he had no clue what was wrong with it.
Here's just a paste of the code if you need it:
var count = 1;
if (framesToTime(time) % 12 == 0) {
count = count + 1
if (count % 2 == 0){
thisProperty = 95
} else {
thisProperty = 20
};
} ;
Ok I don't know why the hell this fixed it, but I changed the name of the variable from "count" to just "x" and it works now. Go figure
Try it.
var count = 1;
if (framesToTime(time) % 12 == 0) {
count = count + 1;
if (count % 2 == 0){
thisProperty = 95;
} else {
thisProperty = 20;
}
}
thisProperty;
In your code, thisProperty has become an ordinary variable. If you write its name at the end of the code, then its value will be assigned to the property.
In AE, if there is nothing inside an if statement or the if statement contains malformed/error code you will receive this error. Put a temp value inside the curly braces or something to process and ensure nothing inside will throw an error.
I also received this error with this:
pastTime = timeToFrames(time)-1;
curPos = transform.xPosition;
pastPos = transform.xPosition.valueAtTime(framesToTime(pastTime));
if (curPos-pastPos[0] != 0) {
// Here is the problem in my case. added a value here 99 to fix until finished testing.
}
else {
effect("Angle Control")("Angle")
}
if/else statements are strict
The syntax for if/else statements is strict in the JavaScript engine
and need to be written for standardized JavaScript.
https://helpx.adobe.com/after-effects/using/expression-language-reference.html*
I got this error because there was a missing semicolon.

if number is between two numbers by plus/minus 10 addclass

http://jsfiddle.net/kM8xE/2/
If I have the divs
​
<div class="value">15</div>
<div class="value2">20</div>​
and jQuery
var actual = $(".value").html();
var comparison = $(".value2").html();
how can i add class .isbetween to .value2 if it's html value is between +/-10 of the html for .value ie. for this eg. a value between 5 and 25.
I am not too good but i have tried and it doesn't work.
if(parseInt(actual)-10 <= parseInt(comparison) <= parseInt(actual)+10){
$(".value2").addClass("isbetween");
}
if (Math.abs(actual - comparison) <= 10) {
//they're within 10
}
The reason this doesn't work is that you can't chain comparisons like this:
5 < x < 10
In Javascript (and other languages with c-like syntax), you have to make two separate comparisons, and use the boolean and operator (&&) to chain the comparisons together:
var actualValue = parseInt(actual);
var comparisonValue = parseInt(comparison);
if(actualValue - 10 <= comparisonValue && comparisonValue <= actualValue + 10) {
$(".value2").addClass("isbetween");
}
Also, don't repeat yourself. Do the conversion once, and store it in a local variable. This makes the code much more readable.
This can be made even more simple by using a concept called absolute value. Then you can just do your difference, and see if its absolute value is less than or equal to ten.
var delta = Math.abs(parseInt(actual) - parseInt(comparison));
if(delta <= 10) {
$(".value2").addClass("isbetween");
}
You have to get the two values, convert them to numbers, compare the absolute value of their difference and then add the class if it meets your condition:
var v1 = +$(".value").text();
var v2 = +$(".value2").text();
if (Math.abs(v1 - v2) <= 10) {
$(".value2").addClass("isbetween");
}

Establishing a minimum value for Javascript variable

I have a series of radio buttons on my page that, when checked, establish certain conditions. For each condition, a value is subtracted from the PRICE, which is the final variable that is provided to the user.
if (condition1==0){price-=5;}
if (condition2==1){price-=4;}
if (condition3==1){price-=10;}
var minimum = 1;
if (price < minimum){price = minimum;)
The conditions are all working exactly as I want. For this code group, I basically don't want "price" to go below 1. My problem is that this is creating an error and I am really not sure why. I am more familiar with Java, so perhaps I am running into an error that Javascript throws that I am not familiar with.
What can I do to make sure that my "price" variable does not fall below my "minimum" variable, without throwing an error?
You have a parentheses ) after price = minimum; rather than a curly bracket } which is why your code is creating an error.
Here is the changes.
Before:
if (price < minimum){price = minimum;)
After:
if (price < minimum){price = minimum;}
^
You could do something like this:
if (condition1 === 0) { price = Math.max(price - 5, 1); }
Or generalize it into a function:
function reducePriceBy(amount) {
return Math.max(price - amount, 1);
}
And then
if (condition1 === 0) { price = reducePriceBy(5); }
You've got a syntax error to start: if (price < minimum){price = minimum;}
The last parentheses should have been a brace.

Why do labels exist?

Why do labels exist in javascript?
var i = 0;
usefulLabel://why do I exist?
while(i <= 10){
document.writeln(i);
i++;
if(i > 5)
break;// usefulLabel;
}
The above code doesn't appear to need a label at all (it works with or without the commented label name). And Considering Douglas Crockford has not condemned them entirely:
Labels
Statement labels are optional. Only these statements should be
labeled: while, do, for, switch.
Are they ever considered a good practice to implement? To me, these things look eerily close to the infamous goto statement in some languages.
If you want to break out of the outermost loop from a nested loop, you need a label.
If you end up needing that, you should consider refactoring the code to make it simpler. (although that won't always be possible)
Yup, they exist for GOTOs and SWITCH statements. I basically see them used for nothing else, and would never consider labeling a section of code just for the fun of it..
The code sample you provided doesn't use the label at all, as it is not referenced in any place.
Read more about labels here:
https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Statements#label_Statement
Here's the example of breaking the loop:
var x = 0;
var z = 0
labelCancelLoops: while (true) {
console.log("Outer loops: " + x);
x += 1;
z = 1;
while (true) {
console.log("Inner loops: " + z);
z += 1;
if (z === 10 && x === 10) {
break labelCancelLoops;
} else if (z === 10) {
break;
}
}
}
I would suggest using the labels to the minimum, as they are confusing to read and follow the flow of the execution. Just like the GOTO.

Elegant way to bias random boolean

I'd like to create a random boolean in JavaScript, but I want to take the previous value into account. If the previous value was true, I want it to be more likely for the next value to be true. At the moment I've got this (this is in the context of a closure - goUp and lastGoUp are locals to the containing scope):
function setGoUp() {
goUp = getRandomBoolean();
if(lastGoUp) {
goUp = getRandomBoolean() || goUp;
}
else {
goUp = getRandomBoolean() && goUp;
}
lastGoUp = goUp;
}
So, the algorithm goes:
Get a random boolean
If the random boolean from the previous call was True:
a) get another random boolean, and or these two together
b) else get another random boolean and and these together.
I'm sure this algorithm could be simplified. I wondered about doing:
if(lastGoUp && goUp) {
goUp = goUp * (getRandomBoolean() || goUp);
}
but that seems really dirty.
There's also a problem with this algorithm which means that I can only double the chance of getting the same boolean again - I can't tweak it easily. Any ideas?
You should define the distribution you want, but maybe you are looking for the following?
if (lastGoUp) {
goUp = Math.random() < 0.8;
} else {
goUp = Math.random() < 0.2;
}
Instead of getting a random boolean, get a random number, say between 0 and 99. Keep a threshold value instead of the last number, and adjust the threshold according to the result:
var threshold = 50;
function setGoUp() {
goUp = getRandomNumber() < threshold;
threshold += goUp ? -10 : 10;
}
This would keep a running tab, so if you get consecutive results that are the same, the probability would keep falling for that result.
If you only want to consider the last result, you would instead set the threshold to a specific value:
threshold = goUp ? 40 : 60;
If you only want the probability of the next event to depend on the current value, and not the history of values up til now, what you want is called a Markov process. Often these are implemented with a 2D table of probabilities that you look up (prob of each next outcome given current one), but for a simple bool-valued event, an if statement is sufficient (see meriton's answer; note that it corresponds to a table of probabilities [0.8 0.2; 0.2 0.8]).
If you want something that gets more likely, say, the more successes you get in a row, then you need to devise a sequence of probabilities for success that perhaps approaches, but does not exceed, 1. There are any number of formulas which can do this, depending on how strong you want the bias to become and how quickly you want it to get there.
I would just make the probability of getting value true be an explicit float variable p. Then I could tweak it easily, by increasing p in some way if I got true last time or by doing nothing with it if I got 'false'.
Can replace Math.random for a better randomizer.
var setGoUp = (function(){
var last;
return function(){
// if last 66% chance for true else 50% chance of true.
return !!(last ? Math.random()*3 : Math.random()*2);
}
}());
!! converts anything to a boolean, 0 = false.

Categories