loading...

Daily Challenge #10 - Calculator

thepracticaldev profile image dev.to staff ・1 min read

Daily Challenge (273 Part Series)

1) Daily Challenge #1 - String Peeler 2) Daily Challenge #2 - String Diamond 3 ... 271 3) Daily Challenge #3 - Vowel Counter 4) Daily Challenge #4 - Checkbook Balancing 5) Daily Challenge #5 - Ten Minute Walk 6) Daily Challenge #6 - Grandma and her friends 7) Daily Challenge #7 - Factorial Decomposition 8) Daily Challenge #8 - Scrabble Word Calculator 9) Daily Challenge #9 - What's Your Number? 10) Daily Challenge #10 - Calculator 11) Daily Challenge #11 - Cubic Numbers 12) Daily Challenge #12 - Next Larger Number 13) Daily Challenge #13 - Twice Linear 14) Daily Challenge #14 - Square into Squares 15) Daily Challenge #15 - Stop gninnipS My sdroW! 16) Daily Challenge #16 - Number of People on the Bus 17) Daily Challenge #17 - Double Trouble 18) Daily Challenge #18 - Triple Trouble 19) Daily Challenge #19 - Turn numbers into words 20) Daily Challenge Post #20 - Number Check 21) Daily Challenge #21 - Human Readable Time 22) Daily Challenge #22 - Simple Pig Latin 23) Daily Challenge #23 - Morse Code Decoder 24) Daily Challenge #24 - Shortest Step 25) Daily Challenge #25 - Double Cola 26) Daily Challenge #26 - Ranking Position 27) Daily Challenge #27 - Unlucky Days 28) Daily Challenge #28 - Kill the Monster! 29) Daily Challenge #29 - Xs and Os 30) Daily Challenge #30 - What is the price? 31) Daily Challenge #31 - Count IPv4 Addresses 32) Daily Challenge #32 - Hide Phone Numbers 33) Daily Challenge #33 - Did you mean...? 34) Daily Challenge #34 - WeIrD StRiNg CaSe 35) Daily Challenge #35 - Find the Outlier 36) Daily Challenge #36 - Let's go for a run! 37) Daily Challenge #37 - Name Swap 38) Daily Challenge #38 - Middle Name 39) Daily Challenge #39 - Virus 40) Daily Challenge #40 - Counting Sheep 41) Daily Challenge #41 - Greed is Good 42) Daily Challenge #42 - Caesar Cipher 43) Daily Challenge #43 - Boardgame Fight Resolver 44) Daily Challenge #44 - Mexican Wave 45) Daily Challenge #45 - Change Machine 46) Daily Challenge #46 - ??? 47) Daily Challenge #47 - Alphabets 48) Daily Challenge #48 - Facebook Likes 49) Daily Challenge #49 - Dollars and Cents 50) Daily Challenge #50 - Number Neighbor 51) Daily Challenge #51 - Valid Curly Braces 52) Daily Challenge #52 - Building a Pyramid 53) Daily Challenge #53 - Faro Shuffle 54) Daily Challenge #54 - What century is it? 55) Daily Challenge #55 - Building a Pile of Cubes 56) Daily Challenge #56 - Coffee Shop 57) Daily Challenge #57 - BMI Calculator 58) Daily Challenge #58 - Smelting Iron Ingots 59) Daily Challenge #59 - Snail Sort 60) Daily Challenge #60 - Find the Missing Letter 61) Daily Challenge #61 - Evolution Rate 62) Daily Challenge #62 - Josephus Survivor 63) Daily Challenge #63- Two Sum 64) Daily Challenge #64- Drying Potatoes 65) Daily Challenge #65- A Disguised Sequence 66) Daily Challenge #66- Friend List 67) Daily Challenge #67- Phone Directory 68) Daily Challenge #68 - Grade Book 69) Daily Challenge #69 - Going to the Cinema 70) Daily Challenge #70 - Pole Vault Competition Results 71) Daily Challenge #71 - See you next Happy Year 72) Daily Challenge #72 - Matrix Shift 73) Daily Challenge #73 - ATM Heist 74) Daily Challenge #74 - Free Pizza 75) Daily Challenge #75 - Set Alarm 76) Daily Challenge #76 - Bingo! (or not...) 77) Daily Challenge #77 - Bird Mountain 78) Daily Challenge #78 - Number of Proper Fractions with Denominator d 79) Daily Challenge #79 - Connect Four 80) Daily Challenge #80 - Longest Vowel Change 81) Daily Challenge #81 - Even or Odd 82) Daily Challenge #82 - English Beggars 83) Daily Challenge #83 - Deodorant Evaporator 84) Daily Challenge #84 - Third Angle of a Triangle 85) Daily Challenge #85 - Unwanted Dollars 86) Daily Challenge #86 - Wouldn't, not Would. 87) Daily Challenge #87 - Pony Express 88) Daily Challenge #88 - Recursive Ninjas 89) Daily Challenge #89 - Extract domain name from URL 90) Daily Challenge #90 - One Step at a Time 91) Daily Challenge #91 - Bananas 92) Daily Challenge #92 - Boggle Board 93) Daily Challenge #93 - Range Extraction 94) Daily Challenge #94 - Last Digit 95) Daily Challenge #95 - CamelCase Method 96) Daily Challenge #96 - Easter Egg Crush Test 97) Daily Challenge #97 - Greed is Good 98) Daily Challenge #98 - Make a Spiral 99) Daily Challenge #99 - Balance the Scales 100) Daily Challenge #100 - Round Up 101) Daily Challenge #101 - Parentheses Generator 102) Daily Challenge #102 - Pentabonacci 103) Daily Challenge #103 - Simple Symbols 104) Daily Challenge #104 - Matrixify 105) Daily Challenge #105 - High-Sum Matrix Drop 106) Daily Challenge #106 - Average Fuel Consumption 107) Daily Challenge #107 - Escape the Mines 108) Daily Challenge #108 - Find the Counterfeit Coin 109) Daily Challenge #109 - Decorate with Wallpaper 110) Daily Challenge #110 - Love VS. Friendship 111) Daily Challenge #111 - 99 Bottles of Beer 112) Daily Challenge #112 - Functions of Integers on the Cartesian Plane 113) Daily Challenge #113 - Iterative Rotation Cipher 114) Daily Challenge #114 - Speed Control 115) Daily Challenge #115 - Look and Say Sequence 116) Daily Challenge #116 - Shortest Knight Path 117) Daily Challenge #117 - MinMinMax 118) Daily Challenge #118 - Reversing a Process 119) Daily Challenge #119 - Adding Big Numbers 120) Daily Challenge #120 - Growth of a Population 121) Daily Challenge #121 - Who has the most money? 122) Daily Challenge #122 - Clockwise Spirals 123) Daily Challenge #123 - Curry me Softly 124) Daily Challenge #124 - Middle Me 125) Daily Challenge #125 - 23 Matches or More 126) Daily Challenge #126 - The Supermarket Line 127) Daily Challenge #127 - Playing with Passphrases 128) Daily Challenge #128 - Blackjack Scorer 129) Daily Challenge #129 - Clay Pigeon Shooting 130) Daily Challenge #130 - GCD Sum 131) Daily Challenge #131 - Remove Anchor from URL 132) Daily Challenge #132 - Is my friend cheating? 133) Daily Challenge #133 - Suitcase Packing 134) Daily Challenge #134 - Rice and Chessboard Problem 135) Daily Challenge #135 - The Wide Mouthed Frog! 136) Daily Challenge #136 - The Deaf Rats of Hamelin 137) Daily Challenge #137 - Help the Bookseller 138) Daily Challenge #138 - Do I get a Bonus? 139) Daily Challenge #138 - Keep Up the Hoop 140) Daily Challenge #140 - I love you, a little, a lot, passionately ... not at all 141) Daily Challenge #141 - Two Sum 142) Daily Challenge #142 - Parts of a Whole 143) Daily Challenge #143 - Big Arithmetic 144) Daily Challenge #144 - Box Office Clerk 145) Daily Challenge #145 - SET Card Game 146) Daily Challenge #146 - The Dollar Game 147) Daily Challenge #147 - NIM 148) Daily Challenge #148 - Disemvowel Trolls 149) Daily Challenge #149 - Fun with Lamps 150) Daily Challenge #150 - Boggle Guess Validator 151) Daily Challenge #151 - Reverse Parentheses 152) Daily Challenge #152 - Strongest Number in an Interval 153) Daily Challenge #153 - Horse Race Gamble 154) Daily Challenge #154 - Stable Arrangement 155) Daily Challenge #155 - Royal Arranged Marriages 156) Daily Challenge #162 - Taxi Dispatching 157) Daily Challenge #163 - Significant Figures 158) Daily Challenge #164 - Jump 159) Daily Challenge #165 - Password Lost in a Grid 160) Daily Challenge #166 - Cat and Mouse 161) Daily Challenge #167 - Return String As Sorted Blocks 162) Daily Challenge #168 - [Code golf] f (f (f b)) = f b 163) Daily Challenge #169 - Christmas Tree 164) Daily Challenge #170 - Pokemon Damage Calculator 165) Daily Challenge #171 - Ordering Beers in Poland 166) Daily Challenge #172 - Find All in an Array 167) Daily Challenge #173 - Pandemia 168) Daily Challenge #174 - Soccer League Table 169) Daily Challenge #175 - Complementary DNA 170) Daily Challenge #176 - Loopover 171) Daily Challenge #177 - Supersize Me 172) Daily Challenge #178 - Create Phone Numbers 173) Daily Challenge #179 - Hide Phone Numbers 174) Daily Challenge #180 - SMS Shortener 175) Daily Challenge #181 - Is King in Check? 176) Daily Challenge #182 - Arrh, grabscrab! 177) Daily Challenge #183 - Automorphic Numbers 178) Daily Challenge #184 - Form the Minimum 179) Daily Challenge #185 - House Numbers Sum 180) Daily Challenge #186 - Jumping Frog 181) Daily Challenge #187 - Most Sales 182) Daily Challenge #188 - Break camelCase 183) Daily Challenge #189 - Convert Number into Reversed Array 184) Daily Challenge #190 - capitalizeFirstLast 185) Daily Challenge #191 - Alphabetical Addition 186) Daily Challenge #192 - Can you Survive the Zombies? 187) Daily Challenge #193 - What's the Real Floor? 188) Daily Challenge #194 - Spread Number 189) Daily Challenge #195 - No Zeroes for Heroes 190) Daily Challenge #196 - Flou 191) Daily Challenge #197 - Population Growth 192) Daily Challenge #198 - 21 Blackjack 193) Daily Challenge #199 - List of All Rationals 194) Daily Challenge #200 - Longest Linear Palindromic Substring 195) Daily Challenge #201 - Complete the Pattern 196) Daily Challenge #202 - Complete the Pattern II 197) Daily Challenge #203 - Pascal's Triangle 198) Daily Challenge #204 - Partial Keys 199) Daily Challenge #205 - Consonant String Value 200) Daily Challenge #206 - Pound Means Backspace 201) Daily Challenge #207 - Snakes and Ladders 202) Daily Challenge #208 - Delete Occurrences of an Element 203) Daily Challenge #209 - Roman Numerals 204) Daily Challenge #210 - Separate Capitalization 205) Daily Challenge #211 - Product Partitions 206) Daily Challenge #212 - DNA to RNA 207) Daily Challenge #213 - Are they the "same"? 208) Daily Challenge #214 - Persistent Bugger 209) Daily Challenge #215 - Difference of 2 210) Daily Challenge #216 - Rainfall 211) Daily Challenge #217 - SMS w/ an Old Phone 212) Daily Challenge #218 - Possible Sides of a Non-Right Triangle 213) Daily Challenge #219 - Compare Strings 214) Daily Challenge #220 - What Dominates Your Array? 215) Daily Challenge #221 - The Merchant of York 216) Daily Challenge #222 - Parse Bank Account Numbers 217) Daily Challenge #223 - Responsible Drinking 218) Daily Challenge #224 - Password Validator 219) Daily Challenge #225 - Square'n'Sum 220) Daily Challenge #226 - Array to HTML Table 221) Daily Challenge #227 - Android Lock Screen 222) Daily Challenge #228 - Best Profit in Single Sale 223) Daily Challenge #229 - Haiku Validator 224) Daily Challenge #230 - Beeramid 225) Daily Challenge #231 - Perfect Powers 226) Daily Challenge #232 - Regex Pattern 227) Daily Challenge #233 - Get Planet Name by ID 228) Daily Challenge #234 - Happy Birthday! 229) Daily Challenge #235 - Reversing a Process 230) Daily Challenge #236 - RGB to Hex Conversion 231) Daily Challenge #237 - Delete Extra Occurrences 232) Daily Challenge #238 - One is the Loneliest Number 233) Daily Challenge #239 - Graceful Tipping 234) Daily Challenge #240 - ATM 235) Daily Challenge #241 - Tip Calculator 236) Daily Challenge #242 - Expressions Matter 237) Daily Challenge #243 - Redacted! 238) Daily Challenge #244 - Search for Letters 239) Daily Challenge #245 - Sort Santa's Reindeer 240) Daily Challenge #246 - Readable Specification Pattern 241) Daily Challenge #247 - Get Planet Name By ID 242) Daily Challenge #248 - Chinese Numerals 243) Daily Challenge #249 - Incremental Changes 244) Daily Challenge #250 - Last Digit of a Large Number 245) Daily Challenge #251 - Largest Number in a Sequence 246) Daily Challenge #252 - Everybody hates Mondays 247) Daily Challenge #253 - Sort Sentences Pseudo-alphabetically 248) Daily Challenge #254 - The Vowel Code 249) Daily Challenge #255 - Is There an Odd Bit? 250) Daily Challenge #256 - How Many Are Smaller Than I? 251) Daily Challenge #257 - Halving Sum 252) Daily Challenge #258 - Ranking Poker Hands 253) Daily Challenge #259 - Duplicate Encoder 254) Daily Challenge #260 - Subtract the Sum 255) Daily Challenge #261 - Diagonal Strings 256) Daily Challenge #262 - No One Likes Spare Change 257) Daily Challenge #263 - Reverse Words 258) Daily Challenge #264 - Digital Root 259) Daily Challenge #265 - Equal Sides 260) Daily Challenge #266 - Who Likes It? 261) Daily Challenge #267 - Braking Speed 262) Daily Challenge #268 - Swapping Characters in Strings 263) Daily Challenge #269 - Decompose n! 264) Daily Challenge #270 - Fix String Case 265) Daily Challenge #271 - Simulate Population Growth 266) Daily Challenge #272 - Printer Errors 267) Daily Challenge #273 - Remove Duplicates 268) Daily Challenge #274 - Aerial Firefighting 269) Daily Challenge #275 - Casino Chips 270) Daily Challenge #276 - Unwanted Dollar Signs 271) Daily Challenge #277 - Over the Road 272) Daily Challenge #278 - Find all non-consecutive numbers 273) Daily Challenge #279 - Playing with Sandpiles

We're back with another code challenge, this one comes from user obrok on Codewars:

Create a simple calculator that given a string of operators (+ - * and /) and numbers separated by spaces returns the value of that expression

Example:

Calculator().evaluate("2 / 2 + 3 * 4 - 6") # => 7

Remember about the order of operations! Multiplications and divisions have a higher priority and should be performed left-to-right. Additions and subtractions have a lower priority and should also be performed left-to-right.

Thank you to CodeWars, who has licensed redistribution of this challenge under the 2-Clause BSD License!

Daily Challenge (273 Part Series)

1) Daily Challenge #1 - String Peeler 2) Daily Challenge #2 - String Diamond 3 ... 271 3) Daily Challenge #3 - Vowel Counter 4) Daily Challenge #4 - Checkbook Balancing 5) Daily Challenge #5 - Ten Minute Walk 6) Daily Challenge #6 - Grandma and her friends 7) Daily Challenge #7 - Factorial Decomposition 8) Daily Challenge #8 - Scrabble Word Calculator 9) Daily Challenge #9 - What's Your Number? 10) Daily Challenge #10 - Calculator 11) Daily Challenge #11 - Cubic Numbers 12) Daily Challenge #12 - Next Larger Number 13) Daily Challenge #13 - Twice Linear 14) Daily Challenge #14 - Square into Squares 15) Daily Challenge #15 - Stop gninnipS My sdroW! 16) Daily Challenge #16 - Number of People on the Bus 17) Daily Challenge #17 - Double Trouble 18) Daily Challenge #18 - Triple Trouble 19) Daily Challenge #19 - Turn numbers into words 20) Daily Challenge Post #20 - Number Check 21) Daily Challenge #21 - Human Readable Time 22) Daily Challenge #22 - Simple Pig Latin 23) Daily Challenge #23 - Morse Code Decoder 24) Daily Challenge #24 - Shortest Step 25) Daily Challenge #25 - Double Cola 26) Daily Challenge #26 - Ranking Position 27) Daily Challenge #27 - Unlucky Days 28) Daily Challenge #28 - Kill the Monster! 29) Daily Challenge #29 - Xs and Os 30) Daily Challenge #30 - What is the price? 31) Daily Challenge #31 - Count IPv4 Addresses 32) Daily Challenge #32 - Hide Phone Numbers 33) Daily Challenge #33 - Did you mean...? 34) Daily Challenge #34 - WeIrD StRiNg CaSe 35) Daily Challenge #35 - Find the Outlier 36) Daily Challenge #36 - Let's go for a run! 37) Daily Challenge #37 - Name Swap 38) Daily Challenge #38 - Middle Name 39) Daily Challenge #39 - Virus 40) Daily Challenge #40 - Counting Sheep 41) Daily Challenge #41 - Greed is Good 42) Daily Challenge #42 - Caesar Cipher 43) Daily Challenge #43 - Boardgame Fight Resolver 44) Daily Challenge #44 - Mexican Wave 45) Daily Challenge #45 - Change Machine 46) Daily Challenge #46 - ??? 47) Daily Challenge #47 - Alphabets 48) Daily Challenge #48 - Facebook Likes 49) Daily Challenge #49 - Dollars and Cents 50) Daily Challenge #50 - Number Neighbor 51) Daily Challenge #51 - Valid Curly Braces 52) Daily Challenge #52 - Building a Pyramid 53) Daily Challenge #53 - Faro Shuffle 54) Daily Challenge #54 - What century is it? 55) Daily Challenge #55 - Building a Pile of Cubes 56) Daily Challenge #56 - Coffee Shop 57) Daily Challenge #57 - BMI Calculator 58) Daily Challenge #58 - Smelting Iron Ingots 59) Daily Challenge #59 - Snail Sort 60) Daily Challenge #60 - Find the Missing Letter 61) Daily Challenge #61 - Evolution Rate 62) Daily Challenge #62 - Josephus Survivor 63) Daily Challenge #63- Two Sum 64) Daily Challenge #64- Drying Potatoes 65) Daily Challenge #65- A Disguised Sequence 66) Daily Challenge #66- Friend List 67) Daily Challenge #67- Phone Directory 68) Daily Challenge #68 - Grade Book 69) Daily Challenge #69 - Going to the Cinema 70) Daily Challenge #70 - Pole Vault Competition Results 71) Daily Challenge #71 - See you next Happy Year 72) Daily Challenge #72 - Matrix Shift 73) Daily Challenge #73 - ATM Heist 74) Daily Challenge #74 - Free Pizza 75) Daily Challenge #75 - Set Alarm 76) Daily Challenge #76 - Bingo! (or not...) 77) Daily Challenge #77 - Bird Mountain 78) Daily Challenge #78 - Number of Proper Fractions with Denominator d 79) Daily Challenge #79 - Connect Four 80) Daily Challenge #80 - Longest Vowel Change 81) Daily Challenge #81 - Even or Odd 82) Daily Challenge #82 - English Beggars 83) Daily Challenge #83 - Deodorant Evaporator 84) Daily Challenge #84 - Third Angle of a Triangle 85) Daily Challenge #85 - Unwanted Dollars 86) Daily Challenge #86 - Wouldn't, not Would. 87) Daily Challenge #87 - Pony Express 88) Daily Challenge #88 - Recursive Ninjas 89) Daily Challenge #89 - Extract domain name from URL 90) Daily Challenge #90 - One Step at a Time 91) Daily Challenge #91 - Bananas 92) Daily Challenge #92 - Boggle Board 93) Daily Challenge #93 - Range Extraction 94) Daily Challenge #94 - Last Digit 95) Daily Challenge #95 - CamelCase Method 96) Daily Challenge #96 - Easter Egg Crush Test 97) Daily Challenge #97 - Greed is Good 98) Daily Challenge #98 - Make a Spiral 99) Daily Challenge #99 - Balance the Scales 100) Daily Challenge #100 - Round Up 101) Daily Challenge #101 - Parentheses Generator 102) Daily Challenge #102 - Pentabonacci 103) Daily Challenge #103 - Simple Symbols 104) Daily Challenge #104 - Matrixify 105) Daily Challenge #105 - High-Sum Matrix Drop 106) Daily Challenge #106 - Average Fuel Consumption 107) Daily Challenge #107 - Escape the Mines 108) Daily Challenge #108 - Find the Counterfeit Coin 109) Daily Challenge #109 - Decorate with Wallpaper 110) Daily Challenge #110 - Love VS. Friendship 111) Daily Challenge #111 - 99 Bottles of Beer 112) Daily Challenge #112 - Functions of Integers on the Cartesian Plane 113) Daily Challenge #113 - Iterative Rotation Cipher 114) Daily Challenge #114 - Speed Control 115) Daily Challenge #115 - Look and Say Sequence 116) Daily Challenge #116 - Shortest Knight Path 117) Daily Challenge #117 - MinMinMax 118) Daily Challenge #118 - Reversing a Process 119) Daily Challenge #119 - Adding Big Numbers 120) Daily Challenge #120 - Growth of a Population 121) Daily Challenge #121 - Who has the most money? 122) Daily Challenge #122 - Clockwise Spirals 123) Daily Challenge #123 - Curry me Softly 124) Daily Challenge #124 - Middle Me 125) Daily Challenge #125 - 23 Matches or More 126) Daily Challenge #126 - The Supermarket Line 127) Daily Challenge #127 - Playing with Passphrases 128) Daily Challenge #128 - Blackjack Scorer 129) Daily Challenge #129 - Clay Pigeon Shooting 130) Daily Challenge #130 - GCD Sum 131) Daily Challenge #131 - Remove Anchor from URL 132) Daily Challenge #132 - Is my friend cheating? 133) Daily Challenge #133 - Suitcase Packing 134) Daily Challenge #134 - Rice and Chessboard Problem 135) Daily Challenge #135 - The Wide Mouthed Frog! 136) Daily Challenge #136 - The Deaf Rats of Hamelin 137) Daily Challenge #137 - Help the Bookseller 138) Daily Challenge #138 - Do I get a Bonus? 139) Daily Challenge #138 - Keep Up the Hoop 140) Daily Challenge #140 - I love you, a little, a lot, passionately ... not at all 141) Daily Challenge #141 - Two Sum 142) Daily Challenge #142 - Parts of a Whole 143) Daily Challenge #143 - Big Arithmetic 144) Daily Challenge #144 - Box Office Clerk 145) Daily Challenge #145 - SET Card Game 146) Daily Challenge #146 - The Dollar Game 147) Daily Challenge #147 - NIM 148) Daily Challenge #148 - Disemvowel Trolls 149) Daily Challenge #149 - Fun with Lamps 150) Daily Challenge #150 - Boggle Guess Validator 151) Daily Challenge #151 - Reverse Parentheses 152) Daily Challenge #152 - Strongest Number in an Interval 153) Daily Challenge #153 - Horse Race Gamble 154) Daily Challenge #154 - Stable Arrangement 155) Daily Challenge #155 - Royal Arranged Marriages 156) Daily Challenge #162 - Taxi Dispatching 157) Daily Challenge #163 - Significant Figures 158) Daily Challenge #164 - Jump 159) Daily Challenge #165 - Password Lost in a Grid 160) Daily Challenge #166 - Cat and Mouse 161) Daily Challenge #167 - Return String As Sorted Blocks 162) Daily Challenge #168 - [Code golf] f (f (f b)) = f b 163) Daily Challenge #169 - Christmas Tree 164) Daily Challenge #170 - Pokemon Damage Calculator 165) Daily Challenge #171 - Ordering Beers in Poland 166) Daily Challenge #172 - Find All in an Array 167) Daily Challenge #173 - Pandemia 168) Daily Challenge #174 - Soccer League Table 169) Daily Challenge #175 - Complementary DNA 170) Daily Challenge #176 - Loopover 171) Daily Challenge #177 - Supersize Me 172) Daily Challenge #178 - Create Phone Numbers 173) Daily Challenge #179 - Hide Phone Numbers 174) Daily Challenge #180 - SMS Shortener 175) Daily Challenge #181 - Is King in Check? 176) Daily Challenge #182 - Arrh, grabscrab! 177) Daily Challenge #183 - Automorphic Numbers 178) Daily Challenge #184 - Form the Minimum 179) Daily Challenge #185 - House Numbers Sum 180) Daily Challenge #186 - Jumping Frog 181) Daily Challenge #187 - Most Sales 182) Daily Challenge #188 - Break camelCase 183) Daily Challenge #189 - Convert Number into Reversed Array 184) Daily Challenge #190 - capitalizeFirstLast 185) Daily Challenge #191 - Alphabetical Addition 186) Daily Challenge #192 - Can you Survive the Zombies? 187) Daily Challenge #193 - What's the Real Floor? 188) Daily Challenge #194 - Spread Number 189) Daily Challenge #195 - No Zeroes for Heroes 190) Daily Challenge #196 - Flou 191) Daily Challenge #197 - Population Growth 192) Daily Challenge #198 - 21 Blackjack 193) Daily Challenge #199 - List of All Rationals 194) Daily Challenge #200 - Longest Linear Palindromic Substring 195) Daily Challenge #201 - Complete the Pattern 196) Daily Challenge #202 - Complete the Pattern II 197) Daily Challenge #203 - Pascal's Triangle 198) Daily Challenge #204 - Partial Keys 199) Daily Challenge #205 - Consonant String Value 200) Daily Challenge #206 - Pound Means Backspace 201) Daily Challenge #207 - Snakes and Ladders 202) Daily Challenge #208 - Delete Occurrences of an Element 203) Daily Challenge #209 - Roman Numerals 204) Daily Challenge #210 - Separate Capitalization 205) Daily Challenge #211 - Product Partitions 206) Daily Challenge #212 - DNA to RNA 207) Daily Challenge #213 - Are they the "same"? 208) Daily Challenge #214 - Persistent Bugger 209) Daily Challenge #215 - Difference of 2 210) Daily Challenge #216 - Rainfall 211) Daily Challenge #217 - SMS w/ an Old Phone 212) Daily Challenge #218 - Possible Sides of a Non-Right Triangle 213) Daily Challenge #219 - Compare Strings 214) Daily Challenge #220 - What Dominates Your Array? 215) Daily Challenge #221 - The Merchant of York 216) Daily Challenge #222 - Parse Bank Account Numbers 217) Daily Challenge #223 - Responsible Drinking 218) Daily Challenge #224 - Password Validator 219) Daily Challenge #225 - Square'n'Sum 220) Daily Challenge #226 - Array to HTML Table 221) Daily Challenge #227 - Android Lock Screen 222) Daily Challenge #228 - Best Profit in Single Sale 223) Daily Challenge #229 - Haiku Validator 224) Daily Challenge #230 - Beeramid 225) Daily Challenge #231 - Perfect Powers 226) Daily Challenge #232 - Regex Pattern 227) Daily Challenge #233 - Get Planet Name by ID 228) Daily Challenge #234 - Happy Birthday! 229) Daily Challenge #235 - Reversing a Process 230) Daily Challenge #236 - RGB to Hex Conversion 231) Daily Challenge #237 - Delete Extra Occurrences 232) Daily Challenge #238 - One is the Loneliest Number 233) Daily Challenge #239 - Graceful Tipping 234) Daily Challenge #240 - ATM 235) Daily Challenge #241 - Tip Calculator 236) Daily Challenge #242 - Expressions Matter 237) Daily Challenge #243 - Redacted! 238) Daily Challenge #244 - Search for Letters 239) Daily Challenge #245 - Sort Santa's Reindeer 240) Daily Challenge #246 - Readable Specification Pattern 241) Daily Challenge #247 - Get Planet Name By ID 242) Daily Challenge #248 - Chinese Numerals 243) Daily Challenge #249 - Incremental Changes 244) Daily Challenge #250 - Last Digit of a Large Number 245) Daily Challenge #251 - Largest Number in a Sequence 246) Daily Challenge #252 - Everybody hates Mondays 247) Daily Challenge #253 - Sort Sentences Pseudo-alphabetically 248) Daily Challenge #254 - The Vowel Code 249) Daily Challenge #255 - Is There an Odd Bit? 250) Daily Challenge #256 - How Many Are Smaller Than I? 251) Daily Challenge #257 - Halving Sum 252) Daily Challenge #258 - Ranking Poker Hands 253) Daily Challenge #259 - Duplicate Encoder 254) Daily Challenge #260 - Subtract the Sum 255) Daily Challenge #261 - Diagonal Strings 256) Daily Challenge #262 - No One Likes Spare Change 257) Daily Challenge #263 - Reverse Words 258) Daily Challenge #264 - Digital Root 259) Daily Challenge #265 - Equal Sides 260) Daily Challenge #266 - Who Likes It? 261) Daily Challenge #267 - Braking Speed 262) Daily Challenge #268 - Swapping Characters in Strings 263) Daily Challenge #269 - Decompose n! 264) Daily Challenge #270 - Fix String Case 265) Daily Challenge #271 - Simulate Population Growth 266) Daily Challenge #272 - Printer Errors 267) Daily Challenge #273 - Remove Duplicates 268) Daily Challenge #274 - Aerial Firefighting 269) Daily Challenge #275 - Casino Chips 270) Daily Challenge #276 - Unwanted Dollar Signs 271) Daily Challenge #277 - Over the Road 272) Daily Challenge #278 - Find all non-consecutive numbers 273) Daily Challenge #279 - Playing with Sandpiles

Posted on by:

thepracticaldev profile

dev.to staff

@thepracticaldev

The hardworking team behind dev.to ❤️

Discussion

markdown guide
 

Alright, I'm gonna be the one who posts this.

const calc = exp => eval(exp)
 

If you add a check to verify the string is well formed, this will probably be the best and simplest answer.

 

Similarly unfair, the solution in R:

calc <- function (expr) {
    eval(parse(text=expr))
}
> calc("2 / 2 + 3 * 4 - 6")
[1] 7

Warning: don't do this.

 

JavaScript

const calculator = operation => {

  // verify that the string with the operation has the right format:
  //   - a number
  //   - optionally followed by 0 or more:
  //       - a space
  //       - an operator (+-*/)
  //       - a space
  //       - another number
  if (!operation.match(/^\d+( [\+\-\/\*] \d+)*$/)) return null;

  // easy solution: now that we know the string has the format that we expect, 
  // return (eval(operation));
  // ...but someone did it already, so let's go the long way :P


  // break the string by spaces
  let ops = operation.split(" ");
  let opsSimple = [];

  // multiplication and division take priority
  // we create a new array that will only have numbers and + or -
  for (x = 0; x < ops.length; x++) {
    if (ops[x] === '*') {
      const val = opsSimple.pop();
      opsSimple.push(val * ops[x+1]);
      x++;
    } else if (ops[x] === '/') {
      // do not allow division by zero!
      if (ops[x+1] === "0") return "Error! Division by zero!";
      const val = opsSimple.pop();
      opsSimple.push(val / ops[x+1]);
      x++;
    } else {
      opsSimple.push(ops[x]);
    }
  }

  // calculate the addtiions and substractions sequentially
  let result = parseInt(opsSimple[0]);
  for (x = 1; x < opsSimple.length; x = x + 2) {
    if (opsSimple[x] === '+') {
      result += opsSimple[x+1];
    } else {
      result -= opsSimple[x+1];
    }
  }

  return result;
}

Live demo on CodePen

 

10 out of 10 challenges! :)

Although I was just able to make 2 using CSS only :-/

 

I'm gonna try to keep doing em each day in July if I can, you game lol?

I'm currently a day behind you since I didn't get this one done yesterday

 
#!/usr/bin/env perl

use strict ;
use warnings ;
use feature qw{ say postderef signatures state } ;
no warnings qw{ experimental::postderef experimental::signatures } ;

use Carp ;

my $string = "2 / 2 + 3 * 4 - 6" ;
say evaluate( $string ) ;
exit ;

sub evaluate( $string) {
        # multiplication
        while ( $string =~ m/(\-?\d+ \* \-?\d+)/ ) {
            my $before = $1 ;
            my ( $i, $j ) = $before =~ m{(\-?\d+)}g ;
            my $k = $i * $j ;
            $string =~ s/\Q$before/$k/mx ;
            }

        # division
        while ( $string =~ m/(\-?\d+ \/ \-?\d+)/ ) {
            my $before = $1 ;
            my ( $i, $j ) = $before =~ m{(\-?\d+)}g ;
            croak 'Divide by zero' if $j == 0;
            my $k = $i / $j ;
            $string =~ s/\Q$before/$k/mx ;
            }

        # addition
        while ( $string =~ m/(\-?\d+ \+ \-?\d+)/ ) {
            my $before = $1 ;
            my ( $i, $j ) = $before =~ m{(\-?\d+)}g ;
            my $k = $i + $j ;
            $string =~ s/\Q$before/$k/mx ;
            }

        # subtraction
        while ( $string =~ m/(\-?\d+ \- \-?\d+)/ ) {
            my $before = $1 ;
            my ( $i, $j ) = $before =~ m{(\-?\d+)}g ;
            my $k = $i - $j ;
            $string =~ s/\Q$before/$k/mx ;
            }

    return $string ;
    }
 

And, I see now that I don't handle non-whole-numbers, which the problem doesn't give me, but could come along anyway. "5 / 4", for example.

And that's entirely a regex problem.

Hrm.

 

Regex was -?\d+, which would handle whole numbers, but with division in the mix, you have to handle the possibility of decimals.

The regex became -?\d+(?:\.\d+)?. (?: indicates it's not matching, so we're not grabbing the fractional aspect independently, \.\d_ matches one dot and however many digits, and )? closes the thing and matches only if found, so that it doesn't have to be a floating point number.

#!/usr/bin/env perl

use strict ;
use warnings ;
use feature qw{ say postderef signatures state } ;
no warnings qw{ experimental::postderef experimental::signatures } ;

use Carp ;

my $string = "2 / 2 + 3 * 4 - 6" ;
say evaluate( $string ) ;
exit ;

# Remember MDAS
sub evaluate( $string) {
        while ( $string =~ m/(-?\d+(?:\.\d+)? \* -?\d+(?:\.\d+)?)/ ) {
            my $before = $1 ;
            my ( $i, $j ) = $before =~ m{(-?\d+(?:\.\d+)?)}g ;
            my $k = $i * $j ;
            $string =~ s/\Q$before/$k/mx ;
            }

        while ( $string =~ m/(-?\d+(?:\.\d+)? \/ -?\d+(?:\.\d+)?)/ ) {
            my $before = $1 ;
            my ( $i, $j ) = $before =~ m{(-?\d+(?:\.\d+)?)}g ;
            exit if $j == 0;
            my $k = $i / $j ;
            $string =~ s/\Q$before/$k/mx ;
            }

        while ( $string =~ m/(-?\d+(?:\.\d+)? \+ -?\d+(?:\.\d+)?)/ ) {
            my $before = $1 ;
            my ( $i, $j ) = $before =~ m{(-?\d+(?:\.\d+)?)}g ;
            my $k = $i + $j ;
            $string =~ s/\Q$before/$k/mx ;
            }

        while ( $string =~ m/(-?\d+(?:\.\d+)? \- \?\d+(?:\.\d+)?)/ ) {
            my $before = $1 ;
            my ( $i, $j ) = $before =~ m{(-?\d+(?:\.\d+)?)}g ;
            my $k = $i - $j ;
            $string =~ s/\Q$before/$k/mx ;
            }

    return $string ;
    }
 

Rust, verbosely:

use std::{
    ops::{Add, Div, Mul, Sub},
    str::FromStr,
};

macro_rules! apply_op {
    ($op:ident, $tokens:ident, $idx:ident) => {{
        $tokens[$idx - 1] =
            Token::Num($tokens[$idx - 1].as_num()?.$op($tokens[$idx + 1].as_num()?));
        $tokens.remove($idx + 1);
        $tokens.remove($idx);
    }};
}

#[derive(Debug)]
enum CalcError {
    Op,
    Input,
}

#[derive(Debug, Clone, Copy, PartialEq)]
enum Op {
    Add,
    Sub,
    Mul,
    Div,
}

impl FromStr for Op {
    type Err = CalcError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "+" => Ok(Op::Add),
            "-" => Ok(Op::Sub),
            "*" => Ok(Op::Mul),
            "/" => Ok(Op::Div),
            _ => Err(CalcError::Input),
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq)]
enum Token {
    Num(i64),
    Op(Op),
}

impl Token {
    fn as_num(&mut self) -> Result<i64, CalcError> {
        match self {
            Token::Num(x) => Ok(*x),
            _ => Err(CalcError::Op),
        }
    }
}

impl FromStr for Token {
    type Err = CalcError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if let Ok(x) = s.parse::<i64>() {
            return Ok(Token::Num(x));
        } else {
            let op = Op::from_str(s)?;
            return Ok(Token::Op(op));
        }
    }
}

fn eval_str(s: &str) -> Result<i64, CalcError> {
    let mut tokens = tokenize(s)?;

    let mut i = 0;

    // do mul/div
    loop {
        // check if there are any
        let ops: Vec<&Token> = tokens
            .iter()
            .filter(|e| **e == Token::Op(Op::Div) || **e == Token::Op(Op::Mul))
            .collect();
        if ops.is_empty() {
            break;
        }

        if let Some(token) = tokens.get(i) {
            if let Token::Op(op) = *token {
                match op {
                    Op::Div => apply_op!(div, tokens, i),
                    Op::Mul => apply_op!(mul, tokens, i),
                    _ => {}
                }
            }
            i += 1;
        } else {
            // start again from the top
            i = 0;
        }
    }

    i = 0;
    // do add/sub
    loop {
        // check if there are any
        let ops: Vec<&Token> = tokens
            .iter()
            .filter(|e| **e == Token::Op(Op::Add) || **e == Token::Op(Op::Sub))
            .collect();
        if ops.is_empty() {
            break;
        }
        if let Some(token) = tokens.get(i) {
            if let Token::Op(op) = *token {
                match op {
                    Op::Add => apply_op!(add, tokens, i),
                    Op::Sub => apply_op!(sub, tokens, i),
                    _ => {}
                }
            }
            i += 1;
        } else {
            // start again from the top
            i = 0;
        }
    }

    // should only be one token left, return it
    Ok(tokens[0].as_num()?)
}

// string to token Vec
fn tokenize(s: &str) -> Result<Vec<Token>, CalcError> {
    let mut ret = Vec::new();
    for c in s.split(' ') {
        ret.push(Token::from_str(c)?);
    }
    Ok(ret)
}

fn main() {
    let input = "2 / 2 + 3 * 4 - 6";
    assert_eq!(eval_str(input).unwrap(), 7);
}
 

Oh macro_rules are something I haven't dug into and explored much yet! Definitely used the built in ones, but haven't written any yet.
Thanks for the example!

 

I was intimidated by them for a while and avoided the topic - tried one once and now they even end up in my daily puzzle solutions :P Truly not complicated to use, and super handy!

 

I recall this from Data Structures, and that there's ambiguity between handle from left and PEMDAS, which is a huge argument for Reverse Polish Notation.

 

For context (and an example I'm hitting in my testing), 4 - 6 - 2. By PEMDAS, they're of equal order, but it really depends on if you want (4 - 6) - 2 == -4 or 4 - (6 - 2) == 0.

With RPN, it's always number number operator, and variations on that are how we get precedence.

4 6 2 - - has 6 2 - in number number operator form and thus becomes 4, simplifying to 4 4 - and then 0.

4 6 - 2 - would get you the 4 6 - first, giving -2, then -2 2 -, which is -4`

Not that I do my regular math like this...

 

Haskell:

import Data.Char (isDigit)

data Token = Num Int | Add | Sub | Mul | Div deriving (Eq, Show)

input :: String
input = "2 / 2 + 3 * 4 - 6"

tokenize :: String -> [String]
tokenize s = filter (/= " ") $ map (:[]) s

parse :: [String] -> [Token]
parse [] = []
parse (t:ts)
    | isDigit $ t !! 0 = Num (read t) : parse ts
    | t == "*" = Mul : parse ts
    | t == "/" = Div : parse ts
    | t == "+" = Add : parse ts
    | t == "-" = Sub : parse ts
    | otherwise = parse ts

division :: [Token] -> [Token]
division [] = []
division [a] = [a]
division (t:ts) =
    case t of
        Num x ->
            case head ts of
                Div ->
                    let
                        (Num y) = head $ tail ts
                    in
                        Num (quot x y) : division (tail $ tail ts)
                _ -> t : division ts
        _ -> t : division ts

multiplication :: [Token] -> [Token]
multiplication [] = []
multiplication [a] = [a]
multiplication (t:ts) =
    case t of
        Num x ->
            case head ts of
                Mul ->
                    let
                        (Num y) = head $ tail ts
                    in
                        Num (x * y) : multiplication (tail $ tail ts)
                _ -> t : multiplication ts
        _ -> t : multiplication ts

addition :: [Token] -> [Token]
addition [] = []
addition [a] = [a]
addition (t:ts) =
    case t of
        Num x ->
            case head ts of
                Add ->
                    let
                        (Num y) = head $ tail ts
                    in
                        Num (x + y) : addition (tail $ tail ts)
                _ -> t : addition ts
        _ -> t : addition ts

subtraction :: [Token] -> [Token]
subtraction [] = []
subtraction [a] = [a]
subtraction (t:ts) =
    case t of
        Num x ->
            case head ts of
                Sub ->
                    let
                        (Num y) = head $ tail ts
                    in
                        Num (x - y) : subtraction (tail $ tail ts)
                _ -> t : subtraction ts
        _ -> t : subtraction ts

evalStr :: String -> Int
evalStr s =
    let
        [(Num x)] = subtraction $ addition $ multiplication $ division $ parse $ tokenize s
    in
        x

Would anyone be able to help me abstract out that operation pattern? I'm a little stumped on how to de-duplicate this code, even though Haskell is great at that.

 
package utils

import (
    "errors"
    "fmt"
    "math"
    "regexp"
    "strconv"
    "strings"
)

func evaluate(expression string) (float64, error) {
    supportedExpressions := []string{"/", "*", "+", "-"}

    expression = strings.Replace(expression, " ", "", len(expression) / 2)

    for _, e := range supportedExpressions {
        for moreExpressionsAvailable(expression, e) {
            firstExpression := findFirstExpression(expression, e)
            calculatedString, e := calculateString(firstExpression)

            if e != nil {
                return 0, e
            }

            expression = strings.Replace(expression, firstExpression, fmt.Sprintf("%0.2f", calculatedString), 1)
        }
    }
    result, e := strconv.ParseFloat(expression, 32)
    return math.Round(result * 100) / 100, e
}

func moreExpressionsAvailable(string string, e string) bool {
    r := regexp.MustCompile("\\" + e)
    return r.MatchString(string)
}

func findFirstExpression(expression string, operation string) string {
    r := regexp.MustCompile("[0-9.]+\\" + operation + "[0-9.]+")
    return r.FindString(expression)
}

func calculateString(expression string) (float64, error) {
    r := regexp.MustCompile("([0-9.]+)([/*+\\-])([0-9.]+)")

    submatches := r.FindStringSubmatch(expression)

    if len(submatches) < 3 {
        return 0, errors.New("not enought data")
    }

    num1, _ := strconv.ParseFloat(submatches[1], 32)
    numb2, _ := strconv.ParseFloat(submatches[3], 32)
    result, e := calculate(num1, numb2, submatches[2])
    return result, e
}

func calculate(num1 float64, num2 float64, operation string) (float64, error) {

    switch operation {
    case "*":
        return num1 * num2, nil
    case "/":
        return num1 / num2, nil
    case "+":
        return num1 + num2, nil
    case "-":
        return num1 - num2, nil
    }

    return 0, errors.New("Unsupported operator")
}
 

My solution in js

calculator = () => {
  return {
    evaluate: (str) => {
      try{
        return eval(str)
      }
      catch(error) {
        return 'Invalid expression'
      }
    }
  } 
}

calculator().evaluate('2 / 2 + 3 * 4 - 6') // 7
calculator().evaluate('2 / 2 + ') // Invalid expression
 

I’m (still) learning Erlang. This is my solution with the given operators and integer numbers. I’m quite satisfied:

-module( calc ).
-export( [ calc/1 ] ).

-include_lib("eunit/include/eunit.hrl").

% expr := term [+|- term]*
% term := factor [*|/ factor]*
% factor := number
% number := -?digit+

skip_ws( [ $\  | Rest ] ) ->
    skip_ws (Rest );
skip_ws( [ $\t | Rest ] ) ->
    skip_ws (Rest );
skip_ws( Rest ) ->
    Rest.

parse_number( [ $- | S ] ) ->
    parse_number( S, undef, -1 );
parse_number( S ) ->
    parse_number( S, undef, 1 ).

parse_number( [ C | Rest ], Value, Sign ) when C >= $0, C =< $9 ->
    NewValue = case Value of
        undef -> 0;
        _ -> Value
    end * 10 + ( C - $0 ),
    parse_number( Rest, NewValue, Sign );
parse_number( Rest, Value, Sign ) ->
    case Value of
        undef -> { invalid, "" };
        _ -> { Sign * Value, Rest }
    end.

parse_factor( S ) ->
    parse_number( S ).

parse_term( S ) ->
    parse_term( S, 1, $* ).

parse_term( S, Value, Op ) ->
    { F1, R1 } = parse_factor( S ),

    NewValue = case Op of
        $* -> Value * F1;
        $/ -> Value / F1
    end,

    R2 = skip_ws( R1 ),
    case R2 of
        [ $* | R3 ] -> parse_term( skip_ws( R3 ), NewValue, $* );
        [ $/ | R3 ] -> parse_term( skip_ws( R3 ), NewValue, $/ );
        _ -> { NewValue, R2 }
    end.

parse_expr( S ) ->
    parse_expr( S, 0, $+ ).

parse_expr( S, Value, Op ) ->
    { F1, R1 } = parse_term( S ),

    NewValue = case Op of
        $+ -> Value + F1;
        $- -> Value - F1
    end,

    R2 = skip_ws( R1 ),
    case R2 of
        [ $+ | R3 ] -> parse_expr( skip_ws( R3 ), NewValue, $+ );
        [ $- | R3 ] -> parse_expr( skip_ws( R3 ), NewValue, $- );
        _ -> { NewValue, R2 }
    end.

calc( S ) ->
    { Result, [] } = parse_expr( skip_ws( S ) ),
    Result.

% TESTS

skip_ws_test_() -> [
    ?_assertEqual( "", skip_ws("") ),
    ?_assertEqual( "", skip_ws(" ") ),
    ?_assertEqual( "", skip_ws("  ") ),
    ?_assertEqual( "", skip_ws( [ $\t ] ) ),
    ?_assertEqual( "ASDF", skip_ws("  ASDF") ),
    ?_assertEqual( "AS DF ", skip_ws( [ $\ , $\t, $\ , $\ , $A, $S, $\ , $D, $F, $\  ] ) )
].

parse_number_test_() -> [
    ?_assertEqual( { 0, "" }, parse_number("0") ),
    ?_assertEqual( { 2, "" }, parse_number("2") ),
    ?_assertEqual( { 42, "" }, parse_number("42") ),
    ?_assertEqual( { -1, "" }, parse_number("-1") ),
    ?_assertEqual( { -145, "" }, parse_number("-145") ),
    ?_assertEqual( { -145, " some stuff" }, parse_number("-145 some stuff") ),
    ?_assertEqual( { invalid, "" }, parse_number("asdf") ),
    ?_assertEqual( { invalid, "" }, parse_number("-") ),
    ?_assertEqual( { invalid, "" }, parse_number("-stuff") ),
    ?_assertEqual( { invalid, "" }, parse_number("- stuff") )
].

parse_term_test_() -> [
    ?_assertEqual( { 6, "" }, parse_term("3 * 2") ),
    ?_assertEqual( { -6, "" }, parse_term("-3 * 2") ),
    ?_assertEqual( { -6, "" }, parse_term("3 * -2") ),
    ?_assertEqual( { 6, "" }, parse_term("-3 * -2") )
].

parse_expr_test_() -> [
    ?_assertEqual( { 5, "" }, parse_expr("3 + 2") ),
    ?_assertEqual( { 1, "" }, parse_expr("3 - 2") ),
    ?_assertEqual( { 1, "" }, parse_expr("3 + -2") ),
    ?_assertEqual( { -1, "" }, parse_expr("-3 - -2") )
].

calc_test_() -> [
    ?_assertEqual( 0, calc("0") ),
    ?_assertEqual( 2, calc("2") ),
    ?_assertEqual( -3, calc("-3") ),
    ?_assertEqual( 128, calc("128  ") ),
    ?_assertEqual( 128, calc(" 128  ") ),
    ?_assertEqual( 14, calc("2 + 3 * 4") ),
    ?_assertEqual( 9.0, calc("6 / 2 * 3") ),
    ?_assertEqual( 1, calc("-1 - -2") ),
    ?_assertEqual( 11, calc("3 * -2 + 17") )
].

To run it:

% erl
1> c(calc).
{ok,calc}
2> calc:test().
  All 33 tests passed.
ok
3> calc:calc("2 + 3 * 4").
14
4> calc:calc("8 / 2 * 4").
16.0

Then I extended it with:

  • floating point numbers;
  • exponent notation (125e-2 = 1.25);
  • powers, with ^;
  • parentheses.

You can find it in this Gist, and try it:

% erl
1> c(calc).
{ok,calc}
2> calc:test().
  All 57 tests passed.
ok
3> calc:calc("1.5 * 2").
3.0
4> calc:calc("12e4 * 2e-3").
240.0
5> calc:calc("3 ^ 4").
81.0
6> calc:calc("2 ^ 0.5").
1.4142135623730951
7> calc:calc("2 + ( 3 * 4 )").
14.0
8> calc:calc("8 / 2 * (2 + 2)").
16.0 % OBVIOUSLY!
9> calc:calc("3^(1/2)").
1.7320508075688772

parse_number got a bit out of hand, but I find the rest quite elegant. I like Erlang.

 
interface Number {
  type: "NUMBER" | string;
  value: number;
}

interface Operation {
  type: "OPERATION";
  value: "*" | "/" | "+" | "-";
}

const OPERATIONS = {
  "*": multiply,
  "/": divide,
  "+": add,
  "-": subtract
} as const;

const parse = (input: string): (Operation | Number)[] =>
  input
    .split(" ")
    .map(
      (input: string): Number | Operation =>
        input === "*"
          ? { type: "OPERATION", value: "*" }
          : input === "/"
          ? { type: "OPERATION", value: "/" }
          : input === "+"
          ? { type: "OPERATION", value: "+" }
          : input === "-"
          ? { type: "OPERATION", value: "-" }
          : { type: "NUMBER", value: Number(input) }
    );

export const combine = ({ value }: Operation, a: Number, b: Number) => ({
  type: "NUMBER",
  value: OPERATIONS[value](a.value, b.value)
});

export const doOperations = (operations: ("*" | "/" | "+" | "-")[]) => (
  input: (Operation | Number)[]
) => {
  let result = input;

  const firstOperationIndex = (list: (Operation | Number)[]) =>
    list.findIndex(
      ({ type, value }) =>
        type === "OPERATION" && operations.includes(value as any)
    );

  const operationsRemaining = (result: (Operation | Number)[]) =>
    ~firstOperationIndex(result);

  while (operationsRemaining(result)) {
    const operationIndex = firstOperationIndex(result);
    const operation = result[operationIndex] as Operation;

    const firstNumberIndex = operationIndex - 1;
    const firstNumber = result[firstNumberIndex] as Number;

    const secondNumberIndex = operationIndex + 1;
    const secondNumber = result[secondNumberIndex] as Number;

    result = result.reduce(
      (result: (Operation | Number)[], current: Operation | Number, index) => {
        return index === operationIndex
          ? [...result, combine(operation, firstNumber, secondNumber)]
          : [firstNumberIndex, secondNumberIndex].includes(index)
          ? result
          : [...result, current];
      },
      []
    );
  }

  return result;
};

export const calculate = pipe(
  parse,
  doOperations(["*", "/"]),
  doOperations(["+", "-"]),
  map(prop("value")),
  head
);
 

Nim.

from strutils import split, parseFloat
from sequtils import foldl, map
import re

type CalcKind = enum
  vkOperator
  vkValue

type
  CalcItem = ref object
    case Kind: CalcKind
    of vkOperator: Operator: string
    of vkValue: Value: float

proc toCalcItem(item: string): CalcItem =
  if item.match(re"\d+"): 
    return CalcItem(Kind: CalcKind.vkValue, Value: parseFloat(item))

  return CalcItem(Kind: CalcKind.vkOperator, Operator: item)

proc calculate(input: string): float =
  var stack: seq[CalcItem] = input.split(" ").map(toCalcItem)
  var backStack: seq[CalcItem]

  while len(stack) > 0:
    let item = stack.pop()

    if item.Kind == CalcKind.vkOperator:
      if item.Operator == "*":
        let curr = backStack.pop()
        let other = stack.pop()
        backStack.add(CalcItem(Kind: CalcKind.vkValue, Value: other.Value * curr.Value))
      elif item.Operator == "/":
        let curr = backStack.pop()
        let other = stack.pop()
        backStack.add(CalcItem(Kind: CalcKind.vkValue, Value: other.Value / curr.Value))
      else:
        backStack.add(item)
    else:
      backStack.add(item)

  while len(backStack) > 2:
    let curr = backStack.pop()
    let operator = backStack.pop()
    let other = backStack.pop()

    if operator.Operator == "+":
      backStack.add(CalcItem(Kind: CalcKind.vkValue, Value: curr.Value + other.Value))
    else:
      backStack.add(CalcItem(Kind: CalcKind.vkValue, Value: curr.Value - curr.Value))

  return foldl(backStack, a + b.Value, 0.0)


when isMainModule:
  echo $calculate("2 / 2 + 3 * 4 - 6") #7
 

Did some cleanup using types. No more string->float->string conversions. Adding old solution below!

from strutils import split, parseFloat
from sequtils import foldl

proc calculate(input: string): float =
  var stack: seq[string] = input.split(" ")
  var backStack: seq[string]

  while len(stack) > 0:
    let item = stack.pop()

    if item == "*":
      let curr = parseFloat(backStack.pop())
      let other = parseFloat(stack.pop())
      backStack.add($(other * curr))
    elif item == "/":
      let curr = parseFloat(backStack.pop())
      let other = parseFloat(stack.pop())
      backStack.add($(other / curr))
    else:
      backStack.add(item)

  while len(backStack) > 2:
    let item = parseFloat(backStack.pop())
    let operator = backStack.pop()
    let other = parseFloat(backStack.pop())

    if operator == "+":
      backStack.add($(item + other))
    else:
      backStack.add($(item - other))

  return foldl(backStack, a + parseFloat(b), 0.0)


when isMainModule:
  echo $calculate("2 / 2 + 3 * 4 - 6") #7
 

a bit late... this is the solution in PHP

function evaluate(string $x){
    return eval('return '.$x.';');
}
echo evaluate("2 / 2 + 3 * 4 - 6");
 

I remember doing this in C in college. It handled Real numbers (even Complex at one point, I believe), supported variables, could graph a function of x and even calculate the derivative. Much fun.

 

I missed this one yesterday! I'll get it done sometime this week though!