0

I'm still new to using Redux, but basically I'm creating a javascript scientific calculator for a learning project, and I'm not quite sure if I'm on the right track. basically, the calculator is going to use a stack in the form of an array with parenthetical groupings within an expression being represented as a reference to another element in the stack which contains the expression contained in the parentheses.

for example, 3 * (4 + 1)/5 + ln(6+8) would be represented by

[
  [4, '*', 'ref:1', '/', 5, '+', 'ln', 'ref:2'], 
  [4, '+', 1], 
  [6, '+', 8]
]

I'm creating various reducers to handle different pieces. For example, one reducer will handle adding operators to the stack, while another will handle adding numbers to the stack. (after adding their respective elements, each reducer will call two functions that are not a part of the Redux store to handle rendering the input and evaluating/rendering the output into strings, which are also parts of the state object).

Now obviously if the user hits, for example, the number 2, and the last element is the number 3, then the result would be to transform the element into the number 32. However, if the last element is 2, and the user hits the pi key, then I would want to add a '*' operator and THEN 3.14159.

Now it wouldn't make sense for the number reducer to handle adding the '*' operator, and I understand from this question that calling dispatch from the reducer is considered to be an "anti-pattern". Is this where Redux-Thunk would step in, or is there some other solution that I should be looking at?

Also, I was reading this article and I'm not quite sure what they mean when they say that two reducers can't share state slices. What exactly is a single slice, and how is one slice differentiated from another within the combineReducers function?

skyboyer
  • 22,209
  • 7
  • 57
  • 64
Tara
  • 389
  • 3
  • 14
  • Unrelated, but having written multiple expression parsers over the years, I'm curious why this approach--I've never seen it before. Nor am I sure why the reducer would want to handle evaluating the expression. Personally I'd keep the text representation of the expression in state (maybe) and that'd be it. – Dave Newton Aug 18 '19 at 12:57
  • @DaveNewton, the stack represents the bulk of the state. The text representation is just that, representation of the state. I've included the, as their own state slices (one for input, one for output), but the stack essentially IS the state. The reducer handles incoming elements and adds them on to the stack appropriately. There is essentially no evaluation, other than calls to the js Math object during output text rendering. As far as the actual parser goes, from my understanding, modern calculators use a similar stack concept to group a discrete set of elements in an expression. – Tara Aug 18 '19 at 19:25
  • The stack is generally a running tally of operations and operands, e.g., (very roughly) `4 * (5 + 10)` would push the `4`, the `*`, the `(`, the `5`, the `+`, the `10`, and when it hits the `)` it would perform the operation so the stack would then be `4`, `*`, `15`. There's no reason to keep expressions that can be evaluated around. Not saying it can't be done this way, I just don't see the reason, and I don't know of any expression parsers that do it this way, although some or many may. – Dave Newton Aug 18 '19 at 20:00
  • My comment about keeping this in Redux state is unrelated--and it might make sense to keep an AST in redux state so you could have multiple representations of it (e.g., the AST graph, a TeX version, etc.), just depends on the needs. This version seems like a stick-shift AST (e.g., everything's manual) with some (IMO) unusual flatness at the top layer. – Dave Newton Aug 18 '19 at 20:03
  • @DaveNewton I see what you're saying and that does make sense. I just hate parsing actual strings though, I personally think it just looks messy, and you would need to be able to parse the input string in that case, b/c if the user hits the backspace, for example, and the last button entered was a close paren, then you would need to go back and reparse what's actually there rather than just storing representations of the actual buttons that were pressed. – Tara Aug 18 '19 at 21:04
  • Although you did give me an idea, I could store the stack arrays in an object that also contains the running total so they wouldn't have to be reevaluated each time. It would be like have both you're suggestion and my idea combined. – Tara Aug 18 '19 at 21:17
  • You'd parse after a debounce or on submission--it's a lot easier to enter a string than to edit and re-package. And the are tons of parsers already as ways to get started. If it's a button-oriented calculator then string parsing wouldn't make any sense (although it could be nice to show the string or TeX version) but you'll ultimately have the same issue with incomplete expressions. – Dave Newton Aug 18 '19 at 21:37
  • @DaveNewton I had created a calculator years ago, when I was first learning js (https://codepen.io/TaraBryn/pen/EXGjrX). It was a freecodecamp project. I had taken a break from is and web development to dive into ai and machine learning. When I came back, they had added a bunch of new modules covering react, redux, and other tools, so what I'm working on now is just refactoring what I did previously with the new tools...it's strictly for learning – Tara Aug 18 '19 at 22:07
  • Btw, I keep track of which expressions on the stack are incomplete with a separate array of indices. The reason it's a button calculator is because that's what the project called for. If it didn't, I probably would have attempted an input parser instead. – Tara Aug 18 '19 at 22:15
  • Personally I'd approach a button calculator in the same way (and allow driving it through keys) since (a) anything complex will be much faster through the keyboard, and (b) you'll end up with an AST anyway. ¯\_(ツ)_/¯ – Dave Newton Aug 18 '19 at 22:20
  • I don't quite understand the "separate array of indices" bit--seems complicated, but if it works :) – Dave Newton Aug 18 '19 at 22:25

1 Answers1

0

So after taking what seemed like forever to learn Middleware and redux-thunk, I came up with a solution. I realize now that the entire stack needs to be handled by a single reducer, but to get around the problem of using recursion mention above within the reducer, I created an action creator that returns a function:

Function addElement(element)
{
  return (dispatch, getState) => {
    dispatch(addToStack(element));
    while (getState().stack.hasOwnProperty('elementsToBeDispatched'))
    {
      dispatch(addToStack(elementsToBeDispatched[0]));
    }
}

Sorry for any typos, I'm writing this out on my phone.

Tara
  • 389
  • 3
  • 14