21
\$\begingroup\$

Background

Roman numeral is a simple number system with the following properties:

  • Each symbol in the system maps to a specific value. (e.g. I = 1, V = 5, X = 10, C = 100)
  • The value of a Roman numeral can be evaluated as follows:
    • First, find all occurrences of adjacent pairs of symbols where a strictly smaller-valued symbol comes first (e.g. IV, XC). Each such pair is evaluated as the backward difference of two values (e.g. IV = 5 - 1 = 4).
    • Then, interpret the remaining symbols as-is and sum all values in it.
      MMCMXCIV = M + M + CM + XC + IV
               = 1000 + 1000 + (1000 - 100) + (100 - 10) + (5 - 1)
               = 2994
      

Task

Your job is to write a function/program \$F\$ that takes a positive integer \$n\$ and generates a code fragment \$F(n)\$ (not necessarily in the same language). The outputted code fragments must have the following properties:

  • \$F(n)\$ as a full program must output the number \$n\$.
  • \$F(a)+\!\!\!+\,F(b)\$ as a program (where \$+\!\!+\$ means concatenation) must output \$a+b\$ if \$a≥b\$, \$b−a\$ otherwise.
  • This must extend to any number of code fragments emitted by your program in the way that \$b−a\$ cases take precedence (think of an arithmetic expression with +s and -s, but the two sides of -s are flipped and it has higher precedence than +).
    • You do not need to consider the cases where three consecutive input numbers are strictly increasing (the equivalent arithmetic expression has two consecutive -s).

Your score is the byte count of \$F\$. Shortest code in bytes wins.

Example

If \$F(10)\$ outputs the code fragment ABC, and \$F(3)\$ outputs the code fragment xyz:

  • The program ABC should output 10.
  • The program xyz should output 3.
  • The program ABCxyz should output 13.
  • The program xyzABC should output 7.
  • The program ABCABCABC should output 30.
  • The program ABCABCxyzxyzABCxyzxyzABC should output 40, since
    [10, 10, 3, 3, 10, 3, 3, 10] => 10 + 10 + 3 + (10-3) + 3 + (10-3) = 40
    

Additionally, if \$F(8)\$ outputs the code fragment ****:

  • The program ABC****xyz should output 10 + 8 + 3 = 21.
  • The program ****ABCxyz should output (10-8) + 3 = 5.
  • The program xyz********ABC should output (8-3) + (10-8) = 7.
  • You do not need to consider the program xyz****ABC, which contains three consecutive strictly increasing numbers (3, 8, 10).
\$\endgroup\$
12
  • 3
    \$\begingroup\$ Sandboxed \$\endgroup\$ – Wezl Mar 8 at 17:09
  • \$\begingroup\$ I think this is like... custom roman numerals? \$\endgroup\$ – Makonede Mar 8 at 17:29
  • 5
    \$\begingroup\$ This looks like implementing “custom Roman numerals”, where instead of the standard IVX... it can be any arbitrary characters. However, how specific characters have values isn’t clearly specified, and neither is the “rules” of the numerals (e.g. do preceding numbers subtract?). Finally, the scoring criteria fully throws me off what I thought the challenge was. This is very unclear, and I’ve voted to close it as such \$\endgroup\$ – caird coinheringaahing Mar 8 at 17:36
  • 4
    \$\begingroup\$ Let me rephrase the challenge: You write a function/program \$F\$ that takes a positive integer \$n\$ and generates a code fragment \$F(n)\$. \$F(n)\$ as a program must output the number \$n\$. \$F(a) ++ F(b)\$ as a program must output \$a+b\$ if \$a \ge b\$, \$b-a\$ otherwise. This must extend to any number of code fragments emitted by your program in the way that \$b-a\$ cases take precedence (think of an arithmetic expression with +s and -s, but the two sides of -s are flipped and it has higher precedence than +). \$\endgroup\$ – Bubbler Mar 9 at 2:33
  • 5
    \$\begingroup\$ "F(n) as a program must output the number n." Does F(n) required to be a full program which prints n, or a function which returns n? Or F(n) may be an expression which evaluated to n? \$\endgroup\$ – tsh Mar 9 at 9:54

12 Answers 12

14
\$\begingroup\$

Bash, 54 48 bytes

-6 bytes by adapting Dominic van Essen's output-prevention trick

printf ":|:
((s+=p<$1?$1-2*p:$1))
p=$1
echo \$s"

This assumes the program must not have any excess output, which turns out to be one of the tricker parts. I expect one of the golfing languages that will output by default at the end of the program would do best here.

Try it online!

Explanation

Example output for 3 concatenated with output for 10 (the concatenated program outputs 7):

:|:
((s+=p<3?3-2*p:3))
p=3
echo $s:|:
((s+=p<10?10-2*p:10))
p=10
echo $s
  • :|: prevents any echo from a previous fragment from outputting
  • s is the sum value so far (implicitly 0 at the start of the program)
  • p is the previous value

Each fragment assumes that the current value is not less than the next value, and thus adds it to the sum. It also checks whether the previous fragment was wrong to assume this (ie. the previous value was less than the current value), and if so corrects the sum by subtracting double the previous value.

\$\endgroup\$
3
  • \$\begingroup\$ Nice. Some explanation would be great (how the generated code looks like, and how the codes interact with each other). \$\endgroup\$ – Bubbler Mar 9 at 6:55
  • 2
    \$\begingroup\$ @Bubbler Added some. \$\endgroup\$ – Vitruvie Mar 9 at 7:06
  • \$\begingroup\$ Yet again, this old Korn Shell programmer is amazed at all the interesting things Bash can do. \$\endgroup\$ – Mark Stewart Mar 11 at 15:20
6
\$\begingroup\$

R, 65 57 bytes

cat("d=e=F;F=c(",scan(),",F);a=sum(F*(-1)^(F<c(e,0)));a")

Try it online!

How it works (original version)

For an example input of 3, the output is:

d=0;F=c(F, 3 );a=sum(F*(-1)^(F<c(F[-1],0)));a

(note: no trailing newline)

What do the different bits do?

  • d=0 = appends to the last code fragment (if any). This has the effect of changing the final a (output the contents of a) to ad=0 (assign zero to variable ad), and thereby supresses output from all non-final code fragments.
  • F=c(F, 3 ) = append the input value to the list F. F is a shortcut in R for FALSE, so it's initialized to zero. So, after all the code fragments are pasted together, F will contain all the input integers, followed by zero.
  • a=sum(F*(-1)^(F<c(F[-1],0))) = calculate the sum of all the input integers, switching the sign of any that are immediately followed by a larger value. Luckily the last value is zero, so this won't happen to the final input integer. Assign the answer to variable a.
  • a = Finally, output the answer a (but if there's a following code fragment, this'll get changed to ad=0 and nothing output yet... see the start)
\$\endgroup\$
1
  • \$\begingroup\$ Nice, I'm stealing your output-prevention trick. \$\endgroup\$ – Vitruvie Mar 9 at 8:27
6
\$\begingroup\$

APL (Dyalog Unicode), 26 24 bytes

Full program. Prompts for number from stdin, and prints program to stdout.

'{⍺←+/⊢ׯ1*2</,∘0⋄⍺,⍵}'⍞

Try it online! (slightly modified version for ease of use)

The generating program simply prepends a special string to the left of the input number (or numbers, actually).

The special string is an anonymous ambivalent (infix/prefix) lambda.

Compound programs will consist of numbers separated by this lambda, with an additional lambda on the far left.

With infix usage, the lambda amounts to simple concatenation.

With prefix usage, the lambda evaluates Roman numbers.

APL evaluates from right to left, so a compound statement will simply concatenate number values until finally the list is evaluated as a Roman number.

The lambda is as follows:

{} dfn; arguments are (left, optional) and (right):

⍺← if the left argument is undefined (i.e. we are being called monadically, as a prefix lambda), give the following tacit function value to it:

+/ the sum of…

 the argument array…

× times (element-wise)…

¯1* negative one raised to the power of (element-wise)…

2</ the pair-wise less-than of…

,∘0 the argument prepended to a zero

 now, with the supplied or the default evaluate:

  ⍺,⍵ with a supplied left argument the concatenation of the arguments

  ⍺,⍵ with the default argument the tacit function applied to the ravel (flattened ― a no-op) right argument

\$\endgroup\$
5
  • \$\begingroup\$ you probably could write the generating code in a golfing language easily that has 1 byte append + unclosed quoted APL \$\endgroup\$ – Wezl Mar 9 at 17:28
  • \$\begingroup\$ @Wezl Wait, does the generating program and the generated snippets not have to be in the same language? \$\endgroup\$ – Adám Mar 9 at 18:04
  • \$\begingroup\$ right, I guess that's not as clear after the question's been edited (though everything else is clearer). \$\endgroup\$ – Wezl Mar 9 at 18:06
  • \$\begingroup\$ @Wezl You should edit again to clarify that. Not at all apparent. \$\endgroup\$ – Adám Mar 9 at 18:09
  • \$\begingroup\$ done, although the main challenge was about golfing the generated code and I mainly expected participants to just use a template to generate the code, which they did. \$\endgroup\$ – Wezl Mar 9 at 18:12
4
\$\begingroup\$

JavaScript (V8), 66 bytes

n=>`t=this;s=~~t.s-(t.p<${n}&&2*p)+(p=${n}),{get x(){print(s)}}.x`

Try it online!

The core idea is putting the print() behind a getter function. If the .x property is accessed it will print the total s. This is placed at the end of the generated code so that it accesses .x on the last iteration and when concatenated it becomes .xt=this (does not access .x).

\$\endgroup\$
4
\$\begingroup\$

Zsh, 34 32 bytes

<<<"((s+=p<$1?$1-2*p:$1));p=$1;"

Try it online Try it online!

Adapted from Vitrivius's Bash answer (go upvote it too!), shorted considerably by taking advantage of Zsh's math function functionality (Math functions are defined to output the result of the last math expression, which means we don't need to worry about supressing output.)

\$\endgroup\$
4
\$\begingroup\$

Perl 5 (-lp), 39 35 bytes

-4 bytes thanks to Dom Hastings

$_='}{$\-=2*$p*($p<'."$_)-(\$p=$_)"
  • -lp switch (enable line ending processing;assume loop like -n but print line also, like sed)
  • generated code starts with }{ : which closes the loop block and start a new block which is executed after the loop
  • -=, $p=.. rewriting so that affectation is executed after operation and comparison

Try it online!

\$\endgroup\$
2
  • 2
    \$\begingroup\$ +1! I think you can also save 4 bytes, by utilising double quotes: Try it online! \$\endgroup\$ – Dom Hastings Mar 9 at 13:45
  • \$\begingroup\$ indeed, so many backslashes needed in the left side didn't check the right \$\endgroup\$ – Nahuel Fouilleul Mar 9 at 19:49
3
\$\begingroup\$

Java, 696 bytes

n->"import java.util.*;import java.io.*;import java.util.jar.*;interface Main{List<Integer>l=new ArrayList();static void main(String[]a)throws Exception{var z=Main.class.getPackageName();for(var e:System.getProperty(\"java.class.path\").split(System.getProperty(\"path.separator\"))){if(!e.endsWith(\".jar\")){var b=new File(e+File.separatorChar+z);var d=b.listFiles();Arrays.sort(d);for(var f:d){var n=f.getName();if(n.endsWith(\".class\")){Class.forName(n.substring(0,n.length()-6));}}}}int s=0;for(int i=0;i<l.size();i++)s+=l.get(i)-(i>0&&l.get(i)>l.get(i-1)?l.get(i-1)*2:0);System.out.print(s);}}\nclass _"+new java.util.Date().getTime()+"_"+System.nanoTime()+"{static{Main.l.add("+n+");}}//"

The Main interface has a static List field which all the generated classes add an integer to in their static initializer. The main method attempts to load all the classes in the package to run their static initializers. F(a) will return different values each time since it tries to make the class names unique using the current time, so F(a) + F(a) cannot be shortened by using a variable to store the result of F(a).

Generator

Generated Program

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

Python 3.7.3 (outputs Python 3.7.3), 174 bytes

import sys;print("""R=%s
class A():
 def __del__(s):print(a)
if"a"in locals():
 if"p"in locals()and p<R:a-=p+p-R
 else:a+=R
if"v"not in locals():v=A();a=R
p=R"""%sys.argv[1])

More readable version of the code:

import sys
print("""
number = %s
class PrintOnExit():
   def __del__(self):
      # This object will only be destroyed when the interpreter exits
      print(result)

if "result" in locals():
   if "previous" in locals() and previous < number:
       result -= previous
       result += number - previous
   else:
       result += number

if "print_on_destroy" not in locals():
    print_on_destroy = PrintOnExit()
    result = number

previous = number
""" % sys.argv[1])
```
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Welcome to Code Golf! \$\endgroup\$ – Redwolf Programs Mar 10 at 19:08
2
\$\begingroup\$

Julia REPL, 50 bytes

adapted from Dominic's answer

x->"L=0;L=[L;$x];a=sum(L.*(-1).^[diff(L).>0;0]);a"

Try it online!

Julia, 68 bytes

Basically the same with a print that erases the previous line (doesn't work in TIO)

x->"L=0;L=[L;$x];print(\"\\r\\e[K\",sum(L.*(-1).^[diff(L).>0;0]));L"

Try it online!

\$\endgroup\$
2
\$\begingroup\$

JavaScript (ES6), generates Charcoal, 61 bytes

f=
n=>`⎚⊞υI${n}IΣEυ⎇‹ι§⁺υ⮌υ⊕κ±ιι`
<input oninput=o.textContent=f(this.value)><pre id=o>

Explanation:

Clears the canvas of the previous output.

⊞υI${n}

Takes the stringified value, casts it back to integer, and pushes it to the predefined empty list.

IΣEυ⎇‹ι§⁺υ⮌υ⊕κ±ιι

Compares each element of the list with the next element, negating the element if it is smaller, then prints the final sum.

Example: F(10) is ⎚⊞υI10IΣEυ⎇‹ι§⁺…υ¹υκ±ιι, while F(3) is ⎚⊞υI3IΣEυ⎇‹ι§⁺υ⮌υ⊕κ±ιι. The last example is therefore ⎚⊞υI10IΣEυ⎇‹ι§⁺υ⮌υ⊕κ±ιι⎚⊞υI10IΣEυ⎇‹ι§⁺υ⮌υ⊕κ±ιι⎚⊞υI3IΣEυ⎇‹ι§⁺υ⮌υ⊕κ±ιι⎚⊞υI3IΣEυ⎇‹ι§⁺υ⮌υ⊕κ±ιι⎚⊞υI10IΣEυ⎇‹ι§⁺υ⮌υ⊕κ±ιι⎚⊞υI3IΣEυ⎇‹ι§⁺υ⮌υ⊕κ±ιι⎚⊞υI3IΣEυ⎇‹ι§⁺υ⮌υ⊕κ±ιι⎚⊞υI10IΣEυ⎇‹ι§⁺υ⮌υ⊕κ±ιι. Try it online!

\$\endgroup\$
2
  • \$\begingroup\$ what encoding is the source in? \$\endgroup\$ – Wezl Mar 10 at 1:30
  • \$\begingroup\$ @Wezl I've used UTF-8. I don't know whether it makes meaningful sense for JavaScript to output using Charcoal's code page. I guess if I wrote the generating program in Charcoal as well, then that would optimise my byte-count. \$\endgroup\$ – Neil Mar 10 at 10:38
1
\$\begingroup\$

Jelly, 18 17 16 15 14 bytes

⁶,y“_< ×ḤƲɼ+ ©

Try it online! The TIO version has an extra closing quote which can be omitted, but that breaks the footer, so I left it in; that's why it displays 15 bytes.

Explanation

⁶,y“_< ×ḤƲɼ+ ©   Main monadic link
⁶                " "
 ,               [" ", n]
  y              In the following string, replace " " with n
   “             {
                   Value implicitly starts at 0
                   Register implicitly starts at 0
    _              Subtract from the current value
          ɼ          the result of applying this to the register
         Ʋ           (
     <                 Is it smaller than n? (no -> 0, yes -> 1)
       ×               Multiply that by
        Ḥ                twice the register
         Ʋ           )
           +       Add n to the current value
             ©       and copy n to the register
                 }
\$\endgroup\$
1
\$\begingroup\$

Python 3, 120 bytes

lambda n:f"""import atexit as a
try:
 z[-1]*=1-2*(z[-1]<{n})
except:
 z=[];a.register(lambda:print(sum(z)))
z+=[{n}]
"""

The lambda function returns the code snippet using a f-string. In the snippet, z is a list of the "Roman Numerals". In the try block, the code negates the last number in z if it is less than the current number n. If z doesn't exist yet an exception is thrown and caught by the except, where z is initialized to an empty list and a lambda function is registered with atexit. Lastly, the new number as appended to z. When the interpreter exits, the registered lambda print the sum of z.

\$\endgroup\$
1
  • \$\begingroup\$ Good answer, though you could make it shorter using a different language for the template. \$\endgroup\$ – Wezl Mar 11 at 14:14

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.