I have a bunch of field to calculate, and I have 3 cases.
Fields are mostly the same, but there is some differences.
Also, calculation to obtain each field may depend on differents variable, so function may have different signature.
In other languages, I would define an interface, use constructors using interface instead of concrete class and use dependency injection.
In nodeJS, I'm a newbie, and I saw there is no such thing that interfaces.
To take a concrete example, I must calculate
I have 2 case:
case sup36 === true
fixedRouting = (CG + CC + CSF * subscribedPower) / 12
case sup36 === false
fixedRouting = (CG + CC) / 12 + subscribedPower * billedCSFCoef * numDayInPeriod / 100
Right now, I add a parameter:
getFixedRouting(isSup36, CG, CC, subscribedPower, billedCSFCoef, numDayInPeriod) {
if isSup{
return (CG + CC + CSF * subscribedPower) / 12
}else{
return (CG + CC) / 12 + subscribedPower * billedCSFCoef * numDayInPeriod / 100
}
},
Problem is I will have more than 2 cases, and I will have to code more cases in the future. This way of coding will also increase the complexity of my code.
How should I do to be able to have 2 switchable implementations of getFixedRouting(...) ?
You can either have different functions for the different cases like this:
function getFixedRoutingSup36(CG, CC, subscribedPower) {
return (CG + CC + CSF * subscribedPower) / 12;
}
function getFixedRoutingNonSup36(CG, CC, subscribedPower, billedCSFCoef, numDayInPeriod) {
return (CG + CC) / 12 + subscribedPower * billedCSFCoef * numDayInPeriod / 100;
}
This can be helpful if the parameters sent to each differ greatly (for example above the sup36 case does not need or use billedCSFCoef or numDayInPeriod).
If you like having one entry point for all of this you can still have the functions split above like that but have this one entry point that you tell people to use:
function getFixedRouting(dataType, CG, CC, subscribedPower, billedCSFCoef, numDayInPeriod) {
switch (dataType) {
case 1:
return getFixedRoutingSup36(CG, CC, subscribedPower);
case 2:
return getFixedRoutingNonSup36(CG, CC, subscribedPower, billedCSFCoef, numDayInPeriod);
case 3:
return someOtherFunction();
}
}
The drawback here is that if your variety of cases take a different set of parameters it can get ugly.
Another variant is to pass the parameters as a Javascript Object so the contents can vary:
function getFixedRouting(data) {
switch (data.type) {
case 1:
return getFixedRoutingSup36(data.CG, data.CC, data.subscribedPower);
case 2:
return getFixedRoutingNonSup36(data.CG, data.CC, data.subscribedPower, data.billedCSFCoef, data.numDayInPeriod);
case 3:
return someOtherFunction();
}
}
Related
I'm making a program that can understands human words, and so far it's going great.
My current standpoint is understanding math equations. You can add, subtract, multiply and divide very easily with as many numbers as you'd like, but I'm wondering how I can do addition then multiply the result, like this:
const source = require("./source.js")
var main = source.interpret(
"add 4 and 4 multiply by 4",
)
source.output(main)
And it should output:
64
Yes I know that there is an easier way of doing this math equation, however in any calculator of any sort you should be able to this kind of switching in any context.
How can I accomplish this?
Here is the full source code;
index.js:
const source = require("./source.js")
var main = source.interpret(
"add 4 and 4 together then multiply the result by 4",
)
source.output(main)
source.js:
function output(main) {
console.log(main)
}
function interpret(str) {
const dl = str.split(' ');
const operator = dl.shift(x => x.includes("add", "subtract", "multiply", "divide"))
const numbers = dl.filter(x => Number(x))
switch (operator) {
case "add":
return numbers.reduce((a, b) => Number(a) + Number(b));
case "subtract":
return numbers.reduce((a, b) => Number(a) - Number(b));
case "multiply":
return numbers.reduce((a, b) => Number(a) * Number(b));
case "divide":
return numbers.reduce((a, b) => Number(a) / Number(b));
}
}
module.exports = {interpret, output}
The main problem with your interpret function is that after finding a single operator, it will perform that operation on all numbers and immediately return. We can’t simply reduce all the numbers using the first operation we find, because it’s possible that some numbers are related to other operations! In the expression add 2 and 2 multiply by 3, the 3 is related to the multiply operation!
This means that we can't process the entire input like that. An alternative is to iterate over the input, and depending on the operator we find, we perform the related action.
To simplify, let's consider that there's only the add operation. What are we expecting next? It could be [number] and [number], but also, it can be by [number]. The first one just adds the two numbers, but in the second, it should add the new number to the last operation.
A side note: your shift and filter functions are parsing the input, and the switch case is interpreting the parsed structure. Your “human language” is actually a programming language! add 2 and 2 is analogous to 2 + 2 in JavaScript, just different. With that, I will introduce you to some programming language theory terms, it can be easier to search for more help if you deep dive in the topic.
Considering the last paragraph, let's refactor interpret:
// from https://stackoverflow.com/questions/175739/how-can-i-check-if-a-string-is-a-valid-number
function isNumeric(str) {
if (typeof str != "string") return false
return !isNaN(str) && !isNaN(parseFloat(str))
}
function interpret(input) {
const tokens = input.split(' ') // in fancy programming language terms,
// this is a lexical analysis step
// note that we are not supporting things like
// double spaces, something to think about!
let state = 0 // we are keeping the results from our operation here
for (i = 0; i < tokens.length; i++) {
const t = tokens[i] // to keep things shorter
switch (t) {
case "add": // remember: there's two possible uses of this operator
const next = tokens[i + 1]
if (next == "by") {
// we should add the next token (hopefully a number!) to the state
state += parseFloat(tokens[i + 2])
i += 2 // very important! the two tokens we read should be skipped
// by the loop. they were "consumed".
continue // stop processing. we are done with this operation
}
if (isNumeric(next)) {
const a = tokens[i + 2] // this should be the "and"
if (a != "and") {
throw new Error(`expected "and" token, got: ${a}`)
}
const b = parseFloat(tokens[i + 3])
state = parseFloat(next) + b
i += 3 // in this case, we are consuming more tokens
continue
}
throw new Error(`unexpected token: ${next}`)
}
}
return state
}
const input = `add 2 and 2 add by 2 add by 5`
console.log(interpret(input))
There's a lot to improve from this code, but hopefully, you can get an idea or two. One thing to note is that all your operations are "binary operations": they always take two operands. So all that checking and extracting depending if it's by [number] or a [number] and [number] expression is not specific to add, but all operations. There's many ways to write this, you could have a binary_op function, I will go for possibly the least maintainable option:
// from https://stackoverflow.com/questions/175739/how-can-i-check-if-a-string-is-a-valid-number
function isNumeric(str) {
if (typeof str != "string") return false
return !isNaN(str) && !isNaN(parseFloat(str))
}
function isOperand(token) {
const ops = ["add", "multiply"]
if (ops.includes(token)) {
return true
}
return false
}
function interpret(input) {
const tokens = input.split(' ') // in fancy programming language terms,
// this is a lexical analysis step
// note that we are not supporting things like
// double spaces, something to think about!
let state = 0 // we are keeping the results from our operation here
for (i = 0; i < tokens.length; i++) {
const t = tokens[i] // to keep things shorter
if (!isOperand(t)) {
throw new Error(`expected operand token, got: ${t}`)
}
// all operators are binary, so these variables will hold the operands
// they may be two numbers, or a number and the internal state
let a, b;
const next = tokens[i + 1]
if (next == "by") {
// we should add the next token (hopefully a number!) to the state
a = state
b = parseFloat(tokens[i + 2])
i += 2 // very important! the two tokens we read should be skipped
// by the loop. they were "consumed".
}
else if (isNumeric(next)) {
const and = tokens[i + 2] // this should be the "and"
if (and != "and") {
throw new Error(`expected "and" token, got: ${and}`)
}
a = parseFloat(next)
b = parseFloat(tokens[i + 3])
i += 3 // in this case, we are consuming more tokens
} else {
throw new Error(`unexpected token: ${next}`)
}
switch (t) {
case "add":
state = a + b
break;
case "multiply":
state = a * b
}
}
return state
}
const input = `add 2 and 2 add by 1 multiply by 5`
console.log(interpret(input)) // should log 25
There's much more to explore. We are writing a "single-pass" interpreter, where the parsing and the interpreting are tied together. You can split these two, and have a parsing function that turns the input into a structure that you can then interpret. Another point is precedence, we are applying the operation in the order they appear in the expression, but in math, multiplication should be done first than addition. All of these problems are programming language problems.
If you are interested, I deeply recommend the book http://craftinginterpreters.com/ for a gentle introduction on writing programming languages, it will definitely help in your endeavor.
I want to make coding about the final score display. If someone has done 10 multiple choice questions and he clicks on the final score button, then his final score will appear along with the description. The score will be made in a range according to the category, namely 1-59 = Under Average, 60-79 = Average, and 80-100 = Above Average.
I've tried coding it but I found error 1176 on line 7 and 11.
Can you help me fix it?
finalscorebutton.addEventListener(MouseEvent.CLICK, finalscore);
function finalscore(event:MouseEvent):void
{
multiplechoicefinalscore.text = sumofscores;
var finalscore:String = finalscore.toString;
finalscore = multiplechoicefinalscore..text;
if(finalscore.toString < 60){
description.text =
"UNDER AVERAGE.";
}
else if(finalscore.toString >= 60 && finalscore.toString <=79){
description.text =
"AVERAGE.";
}
else{
description.text =
"ABOVE AVERAGE.";
}
}
There are multiple syntax and logic errors.
Something.toString is a reference to a method, you probably mean Something.toString() which calls the said method and returns a text representation of whatever Something is.
You don't need a text representation because you want to compare numbers, you need a numeric representation (which is either int, uint or Number).
There are 2 dots in multiplechoicefinalscore..text, what does it even mean?
There is function finalscore and then you define var finalscore, defining things with the same names is a bad idea in general.
You should keep your script formatted properly, otherwise reading it and understanding would be a pain.
So, I assume you have the user's result is in sumofscores. I'm not sure if the script below will actually work as is, but at least it is logically and syntactically correct:
finalscorebutton.addEventListener(MouseEvent.CLICK, onFinal);
function onFinal(e:MouseEvent):void
{
// Ok, let's keep this one, I think you are putting
// the score result into some kind of TextField.
multiplechoicefinalscore.text = sumofscores;
// Get a definitely numeric representation of the score.
var aScore:int = int(sumofscores);
// In terms of logic, putting the complicated condition case
// under the "else" statement will simplify the program.
if (aScore < 60)
{
description.text = "UNDER AVERAGE.";
}
else if (aScore > 79)
{
description.text = "ABOVE AVERAGE.";
}
else
{
description.text = "AVERAGE.";
}
}
How can I implement a function like
f(k+1) = f(k)**2 + 3 * a(k)
with
a(k) = 1 + sin(2*k/1000)
f(1) = 10
when i want to implement functions like f(k+1) ?
So you want is a recursive function, for further reading on this type of thing lookup a recursive factorial function in python. As far as this problem is concerned I think something like this would do it:
import math
def a(k):
return 1 + math.sin(2*k/1000.0)
def f(k):
if k == 1:
return 10
else:
return f(k-1)**2 + 3*a(k-1)
Note: this is basically just a python implementation of Albert Renshaw psuedo-code answer so some credit is due there
Assuming k+1 is a positive integer > 1, you can solve it with iteration like so. For all integers you will have to modify it further, for non-integers you'd do better to ask in math.stackexchange.com how to convert to a generalized form.
Edit: This is C psuedo-code, originally OP had no programming language listed with his question.
-(int)f:(int)kp1 {
if (kp1-1 == 1) {
return 10;
} else {
return pow([self f:kp1-1],2) + 3 * [self a:kp1-1];
}
}
-(int)a:(int)k {
return 1+sin(2*k/1000.0);
}
I m trying this code:
enum SideType
{
Vex;
Cav;
Plano;
}
function drawLense(aLeftType:SideType){
switch (aLeftType)
{
case Cav:
leftCenter = -aRadius - d * 0.5;
case Vex:
leftCenter = -Math.cos(( -90 + offset) * Math.PI / 180) * aRadius-d*0.5;
case Plano:return ;
case Cav, Vex:
points1= drawCurve(1, -90 + offset + trim, 180 - offset * 2 - (trim * 2), leftCenter, aRadius);
_LB = points1[0];
_LA = points1[1];
}
}
But I get an error when compile:
characters 8-16 : This pattern is unused
So, it pointing at case Cav, Vex:
How can I check Cav or Vex in the case above?
EDIT
I found that if I removed case Cav & Case Vex, then case Cav, Vex will work, but this is not what I want, can't I repeat the pattern usage in an or experision?
like (case Cav||Vex)?
case (Cav || Vex) would result into:
src/com/optics/components/Lense.hx:343: characters 8-38 : Case expression must be a constant value or a pattern, not an arbitrary expression
There are only 3 choices for the value of aLeftType, either Vex, Cav, or Plano.
var aLeftType = Vex;
switch (aLeftType)
{
case Cav:
// If aLeftType is `Cav`, run this line.
case Vex:
// If aLeftType is `Vex`, run this line.
case Plano:
// If aLeftType is `Plano`, run this line.
case Cav, Vex:
// If aLeftType is `Vex` or `Plano`, run this line...
// But the first 2 cases already covered `Vex` and `Plano`,
// so it will never be reached.
}
So really, the code of the 4th case will never be run. It is similar to:
if (a == 1) {
trace("a is 1");
} else if (a == 1) {
trace("a is really 1"); // This can never be reached.
}
That means, you have to think again want do you really want to do.
Usually when you want to make the same thing in different situation, you make a function for that :)
function drawLense(aLeftType:SideType){
switch (aLeftType)
{
case Cav:
leftCenter = -aRadius - d * 0.5;
functionCalledIfCavOrVex();
case Vex:
leftCenter = -Math.cos(( -90 + offset) * Math.PI / 180) * aRadius-d*0.5;
functionCalledIfCavOrVex();
case Plano:return ;
}
}
function functionCalledIfCavOrVex(/*...*/){
points1= drawCurve(1, -90 + offset + trim, 180 - offset * 2 - (trim * 2), leftCenter, aRadius);
_LB = points1[0];
_LA = points1[1];
}
Short answer: no way currently, you can only match one enum option in one place(not counting guarded options). So, duplicate your code for each enum option and live a happy life(this code will be also easier to read) or use a seconds switch(which might be shorter and easier in some more complex cases).
try:
case Cav | Vex:
trace("cav or vex");
Hope it helps.
I need to parse comma separated groups(enclosed in brackets) that may have internal groups inside the groups. It should only separate the outside groups.
I have a function that does this:
function lpeg.commaSplit(arg)
local P,C,V,sep = lpeg.P, lpeg.C, lpeg.V, lpeg.P(",")
local p = P{
"S";
S = lpeg.T_WSpace * C(V"Element") * (lpeg.T_WSpace * sep * lpeg.T_WSpace * C(V"Element"))^0 * lpeg.T_WSpace,
Element = (V"Group")^0 * (1 - lpeg.T_Group - sep)^0 * (V"Group" * (1 - lpeg.T_Group - sep)^0)^0 * (1 - sep)^0,
Group = lpeg.T_LGroup * ((1 - lpeg.T_Group) + V"Group")^0 * lpeg.T_RGroup
}^-1
return lpeg.match(lpeg.Ct(p), arg)
end
But the problem is to remove the extra brackets that may enclose the group.
Here is a test string:
[[a,b,[c,d]],[e,[f,g]]]
should parse to
[a,b,[c,d] & [e,[f,g]]
Notice the internal groups are left alone. A simple removal of the extra brackets on the end does not work since you'll end up with a string like a,b,[c,d]],[e,[f,g].
Any ideas how to modify the lpeg grammar to allow for the outside groups?
As I am not expert in making grammars in LPeg, I found this exercise interesting to do...
I couldn't manage to use your grammar, so I went ahead and made my own, with smaller chunks easier to understand and where I could put the captures I needed.
I think I got a decent empirical result. It works on your test case, I don't know if groups can be more deeply nested, etc. The post-processing of the capture is a bit ad hoc...
require"lpeg"
-- Guesswork...
lpeg.T_WSpace = lpeg.P" "^0
lpeg.T_LGroup = lpeg.P"["
lpeg.T_RGroup = lpeg.P"]"
lpeg.T_Group = lpeg.S"[]"
function lpeg.commaSplit(arg)
local P, C, Ct, V, sep = lpeg.P, lpeg.C, lpeg.Ct, lpeg.V, lpeg.P","
local grammar =
{
"S";
S = lpeg.T_WSpace * V"Group" * lpeg.T_WSpace,
Group = Ct(lpeg.T_LGroup * C(V"Units") * lpeg.T_RGroup),
Units = V"Unit" *
(lpeg.T_WSpace * sep * lpeg.T_WSpace * V"Unit")^0,
Unit = V"Element" + V"Group",
Element = (1 - sep - lpeg.T_Group)^1,
}
return lpeg.match(Ct(P(grammar)^-1), arg)
end
local test = "[[a,b,[c,d]],[e,[f,g]]]"
local res = lpeg.commaSplit(test)
print(dumpObject(res))
print(res[1], res[1][1], res[1][2])
local groups = res[1]
local finalResult = {}
for n, v in ipairs(groups) do
if type(v) == 'table' then
finalResult[#finalResult+1] = "[" .. v[1] .. "]"
end
end
print(dumpObject(finalResult))
dumpObject is just a table dump of my own. The output of this code is as follows:
local T =
{
{
"[a,b,[c,d]],[e,[f,g]]",
{
"a,b,[c,d]",
{
"c,d"
}
},
{
"e,[f,g]",
{
"f,g"
}
}
}
}
table: 0037ED48 [a,b,[c,d]],[e,[f,g]] table: 0037ED70
local T =
{
"[a,b,[c,d]]",
"[e,[f,g]]"
}
Personally, I wouldn't pollute the lpeg table with my stuff, but I kept your style here.
I hope this will be useful (or will be a starting point to make you to advance).