Home > Back-end >  How to write a state machine to solve the Count the smiley faces?
How to write a state machine to solve the Count the smiley faces?

Time:10-30

I have solved the problem Conunt the smiley faces, then when I look through the solutions I find that many people use regexp. Then I want write a state machine to implement regexp and solve this problem. But I failed. This is my code:

function countSmileys(smileys) {
  let state = smileyHasValidEye;
  return smileys.filter(smiley => {
    for (let s of [...smiley]) {
      state = state(s);
    }
    return state === true;
  }).length;
}

function smileyHasValidEye(s) {
  if (s === ':' || s === ';') {
    return smileyHasValidNose;
  }
  return smileyHasValidEye;
}

function smileyHasValidNose(s) {
  if (s === '-' || s === '~') {
    return smileyHasValidMouth;
  }
  return smileyHasValidMouth(s);
}

function smileyHasValidMouth(s) {
  if (s === ')' || s === 'D') {
    return true;
  }
  return;
}

console.log(countSmileys([':)', ';(', ';}', ':-D']));
state = state(s);
              ^

TypeError: state is not a function

Then I debugged my code I found the procedure doesn't enter the smileyHasValidNose function. Then I don't know the reason.

CodePudding user response:

The problem is you don't really reset state in between smileys. So the next smiley state will be true which you can't call (it's not a function).

You could use a local variable for state that resets it to the first function (the first step).

function countSmileys(smileys) {
  let firstStep = smileyHasValidEye;
  return smileys.filter(smiley => {
    let state = firstStep;
    for (let s of [...smiley]) {
      state = state(s);
    }
    return state === true;
  }).length;
}

function smileyHasValidEye(s) {
  if (s === ':' || s === ';') {
    return smileyHasValidNose;
  }
  return smileyHasValidEye;
}

function smileyHasValidNose(s) {
  if (s === '-' || s === '~') {
    return smileyHasValidMouth;
  }
  return smileyHasValidMouth(s);
}

function smileyHasValidMouth(s) {
  if (s === ')' || s === 'D') {
    return true;
  }
  return;
}

console.log(countSmileys([':)', ';(', ';}', ':-D']));
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

This code however, will error if there's more on the string besides the smiley (or a partial of the smiley).

I would change smileyHasValidMouth to return false if it doesn't detect a smiley. Just to be more consistent here...

function smileyHasValidMouth(s) {
  if (s === ')' || s === 'D') {
    return true;
  }
  return false;
}

And adjust your loop to exit early if it finds a value that is not a function.

    for (let s of [...smiley]) {
      state = state(s);
      if(typeof state !== 'function') return state;
    }

function countSmileys(smileys) {
  let firstStep = smileyHasValidEye;
  return smileys.filter(smiley => {
    let state = firstStep;
    for (let s of [...smiley]) {
      state = state(s);
      if (typeof state !== 'function') return state;
    }
  }).length;
}

function smileyHasValidEye(s) {
  if (s === ':' || s === ';') {
    return smileyHasValidNose;
  }
  return smileyHasValidEye;
}

function smileyHasValidNose(s) {
  if (s === '-' || s === '~') {
    return smileyHasValidMouth;
  }
  return smileyHasValidMouth(s);
}

function smileyHasValidMouth(s) {
  if (s === ')' || s === 'D') {
    return true;
  }
  return false;
}

console.log(countSmileys([':~(', ':>', ':D', ':(', ':o>', ';)', ':)']));
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

  • Related