10
\$\begingroup\$

This challenge is inspired by Mathematics is fact. Programming is not.


The mathematical notation for a factorial, or a fact is an exclamation mark !. The exclamation mark is also a common symbol for not in many programming languages.

Challenge:

Take a string, containing numerals, and the characters: + ! as input and output the following:

Everything in front of an exclamation mark should be evaluated as a mathematical expression, so 2+2 would be 4.

Everything after a single exclamation mark should be appended as accessories to whatever is in front of it, so: 2+2!5 should give 45, because 2+2=4, and 5 is an accessory. 2+2!5+5 should give 410.

Since ! also means not, anything that's not an accessory after the fact should not be appended. So, 2+2!!5 should give 4, since 5 is not an accessory. Now, not(not(true))==true, so 2+2!!!5 should give 45. 2+2!!5!5+5 should give: 410, because 2+2=4, then followed by a factorial and !5!5+5. The first 5 is not a fact, but 5+5 is after another exclamation mark, and is therefore a fact, yet again.

Clarifications:

  • The exclamation marks will not be adjacent to a + on either side.
  • There will not be leading + for numbers (it's 5, not +5).
  • You may optionally include a leading zero if that's the result of the expression in front of the first !. Both 4 and 04 are accepted output for input: 0+0!4

Executive summary: evaluate each sum (treating ! as separators). Then discard all numbers that appear after an even number of ! (counting from the start of the string). Then remove all !.

Test cases:

!
   <- Empty string

5
5

12!
12

!87
87

!!5
   <- Empty string

5+5!2+2
104

5+5!!2+2
10

1!2!3!4!5!6!7!8!9
12468

10+10!!2+2!!3+3!4+4
208

2!!3!5
25

2!!3!5!7
25

10!!!!!!!5
105

This is so the shortest code in bytes (in each language) wins! Explanations are strongly encouraged!

\$\endgroup\$
  • \$\begingroup\$ umm...lets say we have 2!!3!5 here is or is not 5 an accessory of 3? \$\endgroup\$ – officialaimm Feb 17 '17 at 16:22
  • 3
    \$\begingroup\$ @officialaimm It's 25 (see added test case). More importantly 2!!3!5!7 would still give 25, because there's an even number of ! left of the 7 (so you don't just count the run right in front of the number, but all the ! left of it). \$\endgroup\$ – Martin Ender Feb 17 '17 at 16:27
  • \$\begingroup\$ Can the output be a Mathematica Row? \$\endgroup\$ – ngenisis Feb 17 '17 at 17:28
  • \$\begingroup\$ Um... so this challenge actually has nothing to do with factorials? \$\endgroup\$ – DLosc Feb 18 '17 at 8:37
5
\$\begingroup\$

Retina, 35 31 29 bytes

Saved 4 bytes by taking some inspiration from ETHproductions.

Thanks to Leo for saving another 2 bytes.

\d+|\+
$*
1+
$.&
1`!

!\d*!?

Try it online!

\$\endgroup\$
  • \$\begingroup\$ You can save some bytes in the last lines like this \$\endgroup\$ – Leo Feb 17 '17 at 20:14
  • 1
    \$\begingroup\$ @Leo That's really neat, thank you. :) \$\endgroup\$ – Martin Ender Feb 17 '17 at 20:22
5
\$\begingroup\$

JavaScript (ES6), 58 56 bytes

Saved two bytes thanks to Martin Ender.

let f =
x=>x.replace(/[^!]+/g,eval).replace(/!(\d*)!?\d*/g,"$1")
<input value="2+2!5+5" oninput="try{O.value=f(value)}catch(e){}"><br>
<input id=O value="410" disabled>

Might be improved somehow...

\$\endgroup\$
  • \$\begingroup\$ Nice use of function argument to replace. \$\endgroup\$ – Neil Feb 17 '17 at 16:36
  • \$\begingroup\$ @Neil Thanks, but I found a better way :-) \$\endgroup\$ – ETHproductions Feb 17 '17 at 16:37
  • \$\begingroup\$ Your code snippet is giving me the wrong answer on 1+1!5. I think you forgot to eval the bit before the !. \$\endgroup\$ – Value Ink Feb 17 '17 at 21:08
  • \$\begingroup\$ @ValueInk Ah darn, I don't think there's any easy way to fix that. \$\endgroup\$ – ETHproductions Feb 17 '17 at 21:18
2
\$\begingroup\$

Jelly, 16 bytes

ṣ”!µḢW;m2$VṾ$L¡€

Try it online!

Explanation

The key observation here is that we can run the steps "out of order"; instead of evaluating the sums then ignoring the ones we don't like, we can ignore the sums in invalid positions, then evaluate the rest.

ṣ”!µḢW;m2$VṾ$L¡€
ṣ”!                Split input on '!'
   µ               Set as the new default for missing arguments
    Ḣ              Take the first element, removing it from the default
     W;  $         Cons with
       m2            every odd-numbered element of {the tail of the !-split input}
               €   For each remaining element
          VṾ$      Evaluate and de-evaluate it
             L¡      a number of times equal to its length

Evaluating a sum like "10+10" will evaluate it to a number, e.g. 20, then de-evaluate it to a string, "20". Repeating that process has no additional effect (it's idempotent). Thus, we effectively evaluate every element of the string, except the null string, which remains unevaluated because it has a zero length.

\$\endgroup\$
  • \$\begingroup\$ That's a great trick to conditionally evaluate each item. Could you somehow take the logical AND of each item and its eval-deval'd value? (I assume the empty string is falsy in Jelly) \$\endgroup\$ – ETHproductions Feb 17 '17 at 20:36
  • \$\begingroup\$ @ETHproductions: Jelly's tacitness makes it unlikely that that would save bytes; it prefers to avoid using the same value twice if possible, and if you do want to reuse a value, you normally have to at least put an extra µ in somewhere (and µ doesn't work inside a loop, meaning that you'd need something even more verbose). I did manage to make it work, as ṣ”!µḢW;m2$ȧVṾ$$€, but it isn't any shorter (and has the characteristic pile up of dollar signs that tends to happen when you push Jelly to the edge of its ability to nest control structures.) \$\endgroup\$ – user62131 Feb 17 '17 at 20:48
2
\$\begingroup\$

Jelly, 18 bytes

ṣ”!µḊm2;@ḢW$LÐfVṾ€

Try it online!

How?

ṣ”!µḊm2;@ḢW$LÐfVṾ€ - Main link: string
ṣ”!                - split on '!' characters
   µ               - monadic chain separation (call that x)      e.g. ['1+1','0+0','0+0','0+0','','1+0','','','']
    Ḋ              - dequeue x (all but the leftmost entry of x) e.g.       ['0+0','0+0','0+0','','1+0','','','']
     m2            - modulo 2 index into that result             e.g.       ['0+0',      '0+0',   '1+0',   '']
           $       - last two links as a monad
         Ḣ         -     head x (the leftmost entry of x)        e.g.  '1+1'
          W        -     wrap                                    e.g. ['1+1']
       ;@          - concatenate with reversed arguments         e.g. ['1+1','0+0',      '0+0',   '1+0',   '']
             Ðf    - filter keep:
            L      -     length (keep that have non-zero length) e.g. ['1+1','0+0',      '0+0',   '1+0']
               V   - eval as jelly code (vectorises)             e.g. [  2,    0,          0,       1]
                      Yes, addition is just + and decimal numbers are just strings of digits in Jelly believe it or not!
                Ṿ€ - uneval €ach (creates a string from each one)e.g. [ '2',  '0',        '0'     ,'1']
                      without the € it would uneval the list and hence yield commas too)
                   - implicit print (prints the resulting list [of characters and possibly
                      lists of characters] as if it were all one string.)
\$\endgroup\$
  • \$\begingroup\$ I don't think this works for literal 0+0 in the middle of the input (in a place where it isn't being discarded); it produces the null string, even though it should produce a digit 0. \$\endgroup\$ – user62131 Feb 17 '17 at 20:01
  • \$\begingroup\$ Ah, true - I will have to move to a longer solution :( \$\endgroup\$ – Jonathan Allan Feb 17 '17 at 20:05
  • \$\begingroup\$ Should be fixed up (possibly golfable now). \$\endgroup\$ – Jonathan Allan Feb 17 '17 at 20:31
1
\$\begingroup\$

CJam, 20 bytes

r'!/{_{'+/1b}&}%(\2%

Try it online! (Linefeed-separated test suite.)

\$\endgroup\$
1
\$\begingroup\$

Ruby, 58 56+1 = 59 57 bytes

Uses the -p flag. -2 bytes from Tutleman.

i=0;$_=' '+$_;gsub(/!?([^!]*)/){eval$1if(2>i+=1)||i%2<1}

Try it online! (An extra line of code was added so that it would take all of the input lines and print the output in different lines.)

\$\endgroup\$
  • \$\begingroup\$ I think you can drop the parentheses around eval$1, no? \$\endgroup\$ – Tutleman Feb 18 '17 at 2:38
  • \$\begingroup\$ @Tutleman huh. I don't know what problem I was having that made me add the parens (they weren't present when I started writing the program), but it seems I really can drop them. \$\endgroup\$ – Value Ink Feb 18 '17 at 2:43
0
\$\begingroup\$

Batch, 192 184 bytes

@echo off
set/ps=
call:c . "%s:!=" "%"
echo(%s%
exit/b
:c
set s=
if not %2=="" set/as=%2
:l
shift
shift
if "%1"=="" exit/b
if %1=="" goto l
set/at=%1
set s=%s%%t%
goto l

Having to handle the empty strings is inconvenient.

\$\endgroup\$
0
\$\begingroup\$

Pip, 18 bytes

I think this is the shortest it gets... although I said that about three iterations ago too.

{VaX++v%2+!v}Ma^'!

Takes input as command-line argument. Try it online!

Explanation

                    a is 1st cmdline arg; global variable v is -1 (implicit)
              a^'!  Split a on !
{           }M      Map this function to the resulting list (note that inside function,
                    a is the function arg):
    ++v              Increment v (so that v tracks the 0-based index of the current
                     element)
       %2            We want to keep the elements where v%2 is 1...
         +!v         ... and also v=0, where v%2 is 0, but adding !v makes it 1
  aX                 String-multiply the argument by the above quantity (turning elements
                     we don't want into empty string)
 V                   Eval it (eval'ing empty string gives nil, but that's okay because
                     nil doesn't output anything)
                    Autoprint the resulting list, concatenated together (implicit)
\$\endgroup\$
0
\$\begingroup\$

R, 95 bytes

function(x)for(e in strsplit(gsub('!([^!]*)![^!]*','\\1!',x),'!')[[1]])cat(eval(parse(text=e)))

There is probably some room for improvement but at the moment it is the best I can come up with.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.