Look mom! I made my own (Base 10) numeral system!

21

3

We've all done it, well, maybe not, but making your own alien language and numbering system is a staple of especially fantasy writing, but is mostly just a fun activity.

The task is simple, take two inputs:

  1. An ordered list input of 10 [ten] unique 'numerals' (any printable ASCII character) and interpret them, in order, as the values 0, 1, 2, 3,...,9

    +There are exceptions to what can be a numeral here. Arithmetic operators (+, -, *, /), Parentheses, and spaces cannot be used as one of the numerals.

  2. An arithmetic problem using only those 'numerals'

And output the equivalent integer result in the given form.

Here's an example:

INPUT

abcdefghij

bcd + efg + hij
OUTPUT

bdgi

In the example, the input list (you may choose which form the list comes in) of 'abcdefghij' corresponds to '0123456789' just as 'hjkloiwdfp' would also correspond 1 to 1 with '0123456789' where instead of 'a' associating with zero, 'h' does. The arithmetic following 'translates' to 123 + 456 + 789, which equals 1368. This must then be outputted in the form we gave it, so b (which represents 1) d (for 2) g (for 6) and i (for 8).

TEST CASES

abcdefghij
abc + def - ghij

-gedc
qwertyuiop
qwerty / uiop

e
%y83l;[=9|
(83l * 9) + 8%

y9|8

MORE RULES

  • Standard Loopholes are forbidden!
  • This is code golf, so shortest answers in bytes wins.
  • Must be a full program or function taking the inputs and outputs in whatever format works best for you. (Just cannot add additioal information in the inputs, just 'numerals' and the expression.
  • Use any language you'd like (as long as it complies with other rules)

Bill W

Posted 2019-08-08T15:08:06.610

Reputation: 327

Question was closed 2019-08-11T07:35:53.690

@AdmBorkBork I should change that! Mistake! – Bill W – 2019-08-08T15:16:07.953

Yes, @Veskah, I should have specified (will edit after this comment). This is my first post here, so some mistakes certainly were made! I'll address space as well. – Bill W – 2019-08-08T15:17:34.177

9The 2nd test case suggests that the final output is rounded, otherwise the result would be q.ioiopewioyetqorw.... If so, what kind of rounding should be applied? – Arnauld – 2019-08-08T15:26:05.223

Can we use proper mathematical symbols instead of * and /? – Adám – 2019-08-08T15:27:34.690

1@Arnauld Round to nearest integer. I should have been more clear, thanks for your comment. – Bill W – 2019-08-08T15:29:33.287

@Adám What did you have in mind? I'm trying to limit the number of symbols you can use as they are exceptions to what can be in the numeral system. – Bill W – 2019-08-08T15:31:06.107

@BillW please edit the challenge to clarify that we need to round to the nearest integer. Thanks! – Giuseppe – 2019-08-08T16:02:16.257

1Welcome to the site! How does the / operator work? From the example it seems to be some integer approximation of division but explaining it in the post is preferable to having to guess from incomplete examples. Similarly to what extent must - be supported can the input ever contain two or more -s in a row or a - after another operator? A firm definition of what the input might look like rather than a vague intuition would be very nice. – Post Rock Garf Hunter – 2019-08-08T16:11:50.650

2

To add to @SriotchilismO'Zaic 's point, we also have a sandbox for your benefit and ours; the intent is to let the community help refine challenges before they are posted. Nice idea for a challenge, though!

– Giuseppe – 2019-08-08T16:16:45.363

@BillW You know, the normal symbols: × for multiplication and ÷ for division. – Adám – 2019-08-08T16:21:00.260

3Different languages are likely to evaluate the same equation differently, I'm not sure there is any way around it. For example, T-SQL returns 1 for 5/3, not 2, due to integer division (not rounding). This doesn't invalidate the challenge, but you may have to allow different acceptable answers for the same test case (see my T-SQL answer below). – BradC – 2019-08-08T16:30:39.593

@BradC I thought that was implied. Same thing goes for order of execution. – Adám – 2019-08-08T16:45:30.950

2@Giuseppe Wow, I have browsed this stack for a long while and never knew of that! Would have definitely been helpful, especially as the first time poster (long time listener) that I am. Will keep note for next time! Thanks for your comment and answer. – Bill W – 2019-08-08T17:06:41.780

Oh I just realized, the values starting with 0 were screwing up my math because they were being treated as octal... – Patrick Roberts – 2019-08-09T00:58:07.487

Bill for the last time go play outside! - Mom – Julien Lopez – 2019-08-09T06:55:57.887

2An interesting variation on this would be one that supports any number base, depending on the length of the first string in the input... – Darrel Hoffman – 2019-08-09T13:54:39.477

Answers

11

05AB1E, 10 9 bytes

žh‡.Eò¹Åв

(Now) outputs as a list of characters.

Try it online or verify all test cases.

Explanation:

  ‡        # Transliterate the second (implicit) input, replacing every character of 
           # the first (implicit) input with:
žh         # The builtin "0123456789"
   .E      # Then evaluate it as Elixir code
     ò     # Round it to the nearest integer
      ¹Åв  # And change it back by using a custom base-conversion with the first input as
           # base (which results in a character list)
           # (after which that result is output implicitly)

The new version of 05AB1E is build is build in Elixir. The .E function will call call_unary(fn x -> {result, _} = Code.eval_string(to_string(x)); result end, a), where the Code.eval_string is an Elixir builtin.

Note that the legacy version of 05AB1E won't work for this, because it is build in Python. The numbers with leading 0s won't be evaluated:
See all test cases in the legacy version (which uses the 10-byte version because the Åв builtin is new).

Kevin Cruijssen

Posted 2019-08-08T15:08:06.610

Reputation: 67 575

8

R, 58 bytes

function(d,s,`[`=chartr)'0-9'[d,eval(parse(t=d['0-9',s]))]

Try it online!

Uses character translation chartr to swap the digits, parses and evals the expression, and then chartrs back to the original digits.

If rounding to nearest integer is required, this is

R, 65 bytes

function(d,s,`[`=chartr)'0-9'[d,round(eval(parse(t=d['0-9',s])))]

Try it online!

Giuseppe

Posted 2019-08-08T15:08:06.610

Reputation: 21 077

Using [ as a shorter name for a function with 3 parameters is very smart. Well done. – Robin Ryder – 2019-08-09T06:12:24.773

6

T-SQL, 117 bytes

DECLARE @ CHAR(99)
SELECT @='SELECT TRANSLATE('+TRANSLATE(e,c,'0123456789')+',''0123456789'','''+c+''')'FROM t
EXEC(@)

Line breaks are for readability only.

Input is via a pre-existing table t with text columns c (characters) and e (equation), per our IO rules.

Uses the SQL 2017 function TRANSLATE to switch between characters and generate a string that contains not only the equation, but the code to translate back to the original characters:

SELECT TRANSLATE(123 + 456 + 789,'0123456789','abcdefghij') 

This string is then evaluated using EXEC().

There may be some characters (such as a single quote ') that would break this code; I haven't tested all possible ASCII characters.

Per the challenge, I am evaluating the expression as given, subject to how my language interprets those operators. As such, the second test case returns 1 (w), and not 2 (e), due to integer division.

BradC

Posted 2019-08-08T15:08:06.610

Reputation: 6 099

4

Perl 6, 38 bytes

{*.trans($_=>^10).EVAL.trans(^10=>$_)}

Try it online!

I'm not sure how the rounding is supposed to work. If it rounds at the end then I can add .round for +6 bytes. If the behaviour of / should be different then it may be longer. Takes input curried like f(arithmetic)(numerals)(arithmetic).

Explanation:

{                                    }  # Anonymous codeblock
 *                                      # Returning a whatever lambda
  .trans($_=>^10)       # That translates the numerals to digits
                 .EVAL  # Evaluates the result as code
                      .trans(^10=>$_)   # And translates it back again

Jo King

Posted 2019-08-08T15:08:06.610

Reputation: 38 234

3

Stax, 74 66 65 bytes

┼ö8Q#xóπcM~oÖ÷╦├mî☼yº─▐4ç≥e╘o▄ê‼ø_k╜ø8%N╫ ╗e<.╗P[─╛èA±!xêj«w╠°{B♪

Run and debug it

Stax does not do well here, lacking a true "eval" instruction. It has one that's called "eval" in the docs, but it only works on literal values, not full expressions.

recursive

Posted 2019-08-08T15:08:06.610

Reputation: 8 616

@dana: Good point. I didn't consider that. A fix is likely to cost some bytes, so I'll wait for some clarification before trying to change that behavior. – recursive – 2019-08-09T15:02:28.400

3

Bash, 97 bytes

IFS=''
read S
read O
A=`echo "$O"|tr "$S" 0-9`
printf %0.f `bc<<<"(${A[@]##0})+0.5"`|tr 0-9 "$S"

Could be less if we could truncate, rather than round. Also tricky to handle leading zeroes (as in test case #2) since Bash interprets numbers starting with 0 as octal.

spuck

Posted 2019-08-08T15:08:06.610

Reputation: 649

What is the consensus on using utilities like "bc" and "tr" for golfing? – spuck – 2019-08-09T00:08:14.133

1I'm no expert, but I think those types of answers are typically submitted as something like "bash + coreutils" – Giuseppe – 2019-08-09T00:18:50.830

@Giuseppe tr is part of the coreutils, while bc is not. Nevertheless, bc is a very common tool. Every other command in this answer is bash. – rexkogitans – 2019-08-09T06:21:47.337

-7 bytes stolen from @CM's answer, reducing '0123456789' to '0-9' – spuck – 2019-08-09T18:41:19.093

Defining T is no longer advantageous: $T is only one byte shorter than 0-9, you only use it twice, and you spend 8 bytes to define it. – ruds – 2019-08-10T19:42:35.467

@ruds: Thanks! -6 bytes thanks to you. – spuck – 2019-08-16T17:03:24.133

2

Perl 5 -p, 63 bytes

$p=<>;eval"y/$p/0-9/";s/\b0+\B//g;$_=int.5+eval;eval"y/0-9/$p/"

Try it online!

Takes the expression on the first line of input and the translation list on the second.

Xcali

Posted 2019-08-08T15:08:06.610

Reputation: 7 671

2

Bean, 94 90 bytes

Hexdump

00000000: 53d0 80d6 d800 d3d0 80a0 1f20 8047 53a0  SÐ.ÖØ.ÓÐ. . .GS 
00000010: 1753 d080 d3d0 80a0 5e20 800a a181 8100  .SÐ.ÓÐ. ^ ..¡...
00000020: 40a0 5f52 cac3 4da0 6580 53d0 80a0 5d20  @ _RÊÃM e.SÐ. ] 
00000030: 8089 205f a065 205f 2080 0aa1 8181 0123  .. _ e _ ..¡...#
00000040: 0058 0020 800a a181 8102 40a0 6550 84a0  .X. ..¡...@ eP. 
00000050: 5d20 652e dce2 b02b dc64                 ] e.Üâ°+Üd

JavaScript

`${Math.round(
  eval(
    b.replace(
      /./g,
      c => ~(i = a.indexOf(c)) ? i : c
    ).replace(
      /\b0+/g,
      ''
    )
  )
)}`.replace(
  /\d/g,
  i => a[i]
)

Explanation

This program implicitly assigns the first and second lines of input as strings to the variables a and b respectively.

Each character c on line b is replaced with the respective index i of the character found on line a, or itself if it is not found.

Then it removes each sequence of one or more 0s preceded by a boundary from the resulting string. This is to prevent eval() from evaluating any sequence of digits starting with 0 as an octal literal.

After eval() and Math.round(), the result is coerced back into a string and each digit character i is replaced by the corresponding character from line a at index i.

Test Cases

Demo

abcdefghij
abcd + efg + hij

bdgi

Demo

abcdefghij
abc + def - ghij

-gedc

Demo

qwertyuiop
qwerty / uiop

e

Demo

%y83l;[=9|
(83l * 9) + 8%

y9|8

Patrick Roberts

Posted 2019-08-08T15:08:06.610

Reputation: 2 475

1

Perl 5, 130 bytes

sub f{eval sprintf"'%.0f'=~y/%s/%s/r",eval(eval(sprintf"\$_[1]=~y/%s/%s/r",@r=map"\Q$_",$_[0],'0123456789')=~s,\b0,,gr),reverse@r}

Try it online!

Maybe that double eval somehow can be turned into s/.../.../geer.

Kjetil S.

Posted 2019-08-08T15:08:06.610

Reputation: 1 049

1

Charcoal, 14 bytes

⍘UV⭆η⎇№θι⌕θιιθ

Try it online! Link is to verbose version of code. Note: Expression is evaluated according to Python 3 semantics, so for instance leading zeros on non-zero numbers are illegal. Explanation:

   ⭆η           Map over expression's characters and join
        ι       Current character
      №θ        Count matches in first input
     ⎇          If non-zero
         ⌕θι    Replace with position in first input
            ι   Otherwise keep character unchanged
 UV             Evaluate as Python 3
⍘            θ  Convert to base using first input as digits

Neil

Posted 2019-08-08T15:08:06.610

Reputation: 95 035

Unfortunately leading 0s don't work in Python, which is present in the test cases. – Jonathan Allan – 2019-08-08T22:56:32.580

0

Python 3, 167 bytes

import re
a=[*enumerate(input())]
e=input()
for i,c in a:e=re.sub(c,str(i),e)
e=str(round(eval(re.sub(r'\b0+(?!\b)','',e))))
for i,c in a:e=re.sub(str(i),c,e)
print(e)

Try it online!

Room for improvement...

movatica

Posted 2019-08-08T15:08:06.610

Reputation: 635

0

Lua, 162 151 150 bytes

  • -11 bytes thanks to my idea of using load instead of function(...) end
  • -1 byte by omitting newline
l,p=...print(((math.ceil(load('return '..p:gsub('.',load'n=l:find(...,1,1)return n and n-1'))()-0.5)..''):gsub('%d',load'c=...+1 return l:sub(c,c)')))

Try it online!

Not shortest thing in the world (Lua forces you to be fancy quite hard, especially by huge keywords), but was pretty fun to create. Full program taking input as arguments and printing result.

Explanation

Introduction

l,p=...

Assign values from arguments to variables. Our dictionary is l and expression is p.

Following expression is quite hard to understand because it have a weird order of execution, so I'll explain it step-by-step:

Converting to normal numbers

p:gsub('.',
load'n=l:find(...,1,1)return n and n-1')

Perform replacement on expression string: take each symbol and pass it to function (load proved itself to be shorted than normal declaration here).

Function finds position of occurrence in dict string for passed symbol using find. ... is first (and only) argument here as we're in vaarg function (any loaded one is) that is our current symbol. Following arguments are required to make find ignore special symbols (1 is just short value which evaluates as true when converted to boolean): starting position (one is default here) and plain, which actually disables pattern handling. Without those program fails on third test case due to % being special.

If match is found, subtract one as Lua strings (and arrays btw) are 1-based. If no match is found, it will return nothing, resulting in no replacement being done.

Solving

math.ceil(load('return '..ABOVE)()-0.5)

Prepend return to our expression to let it return result, calculate it by compiling as Lua function and calling it, perform rounding (this turned other way around to make it shorter).

It the end we get a numerical solution to our problem, only converting it back remains.

Making it crazy again

(ABOVE..'')
:gsub('%d',load'c=...+1 return l:sub(c,c)')

First line is a short way to convert number to string, so now we can call string methods in a short way. Let's do so!

Now gsub is called again to replace everything back to insanity. This time %d is used instead of . as replacement pattern as our function may and must process only numbers (. would result to error on negative numbers). This time function (loaded again to save bytes) first adds 1 to its first (and only) vaargument, converting it to position in dict string, then returns character from it at that position.

Hooray, almost there!

Dramatic finale, or Why Brackets Matter

print((ABOVE))

Well… why two pairs of brackets anyways? It's time to talk about parall… eh, multiple return in Lua. Thing is that one function may return few values from one call (look at this meta question for more examples).

Here, last gsub returned two values: answer string we need and amount of replacements done (count of digits actually, but who cares). If it wasn't for internal pair, both string and number would be printed, screwing us up. So here we sacrifice two bytes to omit second result and finally print product of this insanity factory.


Well, I've enjoyed explaining almost as much as golfing in first place, hope you got what's going on here.

val says Reinstate Monica

Posted 2019-08-08T15:08:06.610

Reputation: 409

Note: it passes all of testcases, but probably rounds incorrectly in others. If you can find one, I'm going to fix it. – val says Reinstate Monica – 2019-08-09T22:02:36.263

0

Python 3, 137 bytes

A non-regex approach using str.translate and str.maketrans to replace the characters. I lost a lot of characters on trimming the leading zeros...

lambda s,t,d='0123456789',e=str.translate,m=str.maketrans:e(str(round(eval(' '.join(c.lstrip('0')for c in e(t,m(s,d)).split())))),m(d,s))

Try it online!

Gábor Fekete

Posted 2019-08-08T15:08:06.610

Reputation: 2 809

0

Wolfram Language (Mathematica), 121 bytes

I define a pure function with two arguments. Since some functions are repeated I save them in a variable to save a few characters. This code simply does some string replacements and then uses ToExpression to evaluate the expression with the Wolfram kernel.

(r=Thread[StringPartition[#,1]->(t=ToString)/@Range[0,9]];u[(u=StringReplace)[#2,r]//ToExpression//Round//t,Reverse/@r])&

Try it online!

MannyC

Posted 2019-08-08T15:08:06.610

Reputation: 131