84
\$\begingroup\$

Duolingo, the language learning app, has a lot of things going for it, but there is one major issue that drives me crazy. It tells me how many days in a row I've used the app with a message like You're on a 7 day streak! Setting aside hyphenation and whether the number should be spelled out, this works fine for most numbers, but is indisputably wrong when it says You're on a 8 day streak! I'm not using it to learn English but this is still unfortunate behavior for a language app.

You're going to help out the Duolingo team by writing a complete program or function that figures out whether a given number should be preceded by a or an. A number is preceded by a if its pronunciation in spoken English begins with a consonant or semivowel sound, and preceded by an if its pronunciation begins with a vowel sound. Thus the only numbers preceded by an are those whose pronunciation begins with eight, eleven, eighteen, or eighty.

Presumably the Duolingo dev team left this bug in because they ran out of space for more source code in the app, so you need to make this code as short as possible in the hopes they can squeeze it in.

Your code must take an integer from 0 to 2,147,483,647 and output a or an. A trailing newline is optional. For the purposes of this challenge, 1863 is read as one thousand eight hundred and sixty-three, not eighteen hundred and sixty-three.

Test cases:

0 → a
8 → an
11 → an
18 → an
84 → an
110 → a
843 → an
1111 → a
1863 → a
8192 → an
11000 → an
18000 → an
110000 → a
180000 → a
1141592 → a
1897932 → a
11234567 → an
18675309 → an
\$\endgroup\$
15
  • 31
    \$\begingroup\$ Is this endorsed by Duolingo? If not, you should get them to pay us for improving the language on a language learning site. \$\endgroup\$
    – Arc676
    Dec 17, 2015 at 7:38
  • 11
    \$\begingroup\$ Is 1100 (an) eleven hundred or (a) one thousand and one hundred? \$\endgroup\$ Dec 17, 2015 at 8:04
  • 12
    \$\begingroup\$ Bilbo would disagree with some of your test cases. :) \$\endgroup\$ Dec 17, 2015 at 9:26
  • 10
    \$\begingroup\$ @Zaibis: "one" here is pronounced like "wun", which has a consonant sound. Hence, "a one thousand and one hundred day streak". \$\endgroup\$ Dec 17, 2015 at 10:26
  • 34
    \$\begingroup\$ They probably left this bug because they thought no one would reach a 8 day streak. \$\endgroup\$
    – Aloha
    Dec 17, 2015 at 11:36

28 Answers 28

29
\$\begingroup\$

Python 2, 60 bytes

lambda n:'a'+'n'[:`n`[0]=='8'or`n`[:2]in len(`n`)%3/2*'118']

An anonymous function. Adds an n if either:

  • The first digit is 8
  • The first two digits are 11 or 18, and the length is 2 modulo 3.
\$\endgroup\$
3
  • \$\begingroup\$ I know this is a super old question, but I think `` n>='8' `` saves three bytes. \$\endgroup\$
    – Lynn
    May 24, 2018 at 18:01
  • \$\begingroup\$ @Lynn Won't that mess up nines though? \$\endgroup\$
    – xnor
    May 24, 2018 at 18:05
  • \$\begingroup\$ Oh, of course! I was fooled by the test suite :) \$\endgroup\$
    – Lynn
    May 24, 2018 at 18:48
16
\$\begingroup\$

Pyth, 23 bytes

<>n\8hz}hjsz^T3,hT18"an

This selects how many letters to slice off the end of "an" by checking whether the first letter is not an 8 and that the first digit of the number when considered in base 1000 is neither 11 nor 18. The resulting boolean is the number of characters to slice of the end.

\$\endgroup\$
1
  • 4
    \$\begingroup\$ Very creative. Also, terrifying. \$\endgroup\$
    – Hellreaver
    Dec 20, 2015 at 12:50
12
\$\begingroup\$

GNU Sed, 32

Score includes +1 for -E option to sed.

s/^8.*|^1[18](...)*$/an/
t
ca
:

Try it online.

  • Remove groups of 3 digits from the end of each number until there is only 1 to 3 digits left
  • Match any number starting with 8 or exactly 11 or 18 and change to an
  • Change all other numbers to a

Thanks to @MartinBüttner for his retina approach that saved 10 bytes.

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

Shell + bsd-games, 30

number -l|sed '/^e/{can
q};ca'

Input read from STDIN.

number converts a decimal string into words. It is then a simple matter to decide whether or not the result begins with e.

\$\endgroup\$
2
  • 2
    \$\begingroup\$ +1 for using bsd-games, I didn't actually think they'd ever be useful :) \$\endgroup\$ Dec 17, 2015 at 15:38
  • \$\begingroup\$ @ASCIIThenANSI yep, bsd-games are useful here and there :) \$\endgroup\$ Dec 17, 2015 at 17:28
9
\$\begingroup\$

Retina, 27 bytes

This isn't very different from DigitalTrauma's Retina answer, but they insisted I post this myself.

^8.*|^1[18](...)*$
an
\d+
a

Try it online.

The first regex replaces all relevant numbers with an, and the second replaces all remaining numbers with a. This works for the same bytes:

^8.*|^1[18](...)*$
n
^\d*
a
\$\endgroup\$
3
  • 1
    \$\begingroup\$ +1 this is almost the same level of abuse of regex as primality testing :) \$\endgroup\$
    – cat
    Dec 17, 2015 at 12:07
  • 1
    \$\begingroup\$ And the good thing is, Duolingo is actually written in Retina so it should be a breeze to integrate this. Or wait, what language was it? \$\endgroup\$ Dec 18, 2015 at 18:22
  • 1
    \$\begingroup\$ @ceasedtoturncounterclockwis I'm being told it's actually written in Hexagony, but they wrote a Retina-to-Hexagony transpiler, so this shouldn't be an issue. \$\endgroup\$ Dec 18, 2015 at 18:24
6
\$\begingroup\$

C++, 101

This is my challenge, so this isn't meant to be a competitive answer. Just wanted to see how short I could get it in C++. String operations are just too verbose so this is done with math. I feel like there must be a way to get that condition smaller but I can't quite figure it out.

const char*f(int i){int n=0,d=0;for(;i;(!(d++%3)&(i==18|i==11))|i==8?n=1:0,i/=10);return n?"an":"a";}
\$\endgroup\$
4
\$\begingroup\$

Mathematica, 53 bytes

If[#~IntegerName~"Words"~StringStartsQ~"e","an","a"]&

A solution using string processing would actually end up being longer.

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

PostScript, 119 113 characters

10 string cvs dup 0 get 56 eq exch dup length 3 mod 2 eq{0 2 getinterval dup(11)eq exch(18)eq or or}{pop}ifelse

With test code:

/An
{
    10 string cvs dup 0 get 56 eq exch dup length 3 mod 2 eq{0 2 getinterval dup(11)eq exch(18)eq or or}{pop}ifelse
} def

/ShouldBeFalse [ 0 110 1111 1863 110000 180000 1141592 1897932 ] def
/ShouldBeTrue [ 8 11 18 84 843 8192 11000 18000 11234567 18675309 ] def

() = (ShouldBeFalse) = ShouldBeFalse {An =} forall
() = (ShouldBeTrue)  = ShouldBeTrue  {An =} forall
\$\endgroup\$
3
\$\begingroup\$

JavaScript (ES6) 70 61 46 38 bytes

n=>/^8|^1[18](...)*$/.test(n)?'an':'a'

Community wiki because the current solution is so different than my original. Thanks everyone!

Demo: http://www.es6fiddle.net/iio40yep/

\$\endgroup\$
15
  • 1
    \$\begingroup\$ that makes sense. Thanks for explaining. \$\endgroup\$
    – Daniel F
    Dec 18, 2015 at 2:54
  • 1
    \$\begingroup\$ @Pavlo Very nice, I forgot about single expressions after discovering the eval trick! I knew there had to be a better regular expression as well, but I couldn't figure anything out that was shorter. Thank you! \$\endgroup\$
    – Scott
    Dec 19, 2015 at 20:43
  • 1
    \$\begingroup\$ @Pavlo Sweet, updated again! Learning lots, thank you very much :) \$\endgroup\$
    – Scott
    Dec 20, 2015 at 2:56
  • 2
    \$\begingroup\$ URGH! Forgot to shave 2 bytes! Here is the final one: n=>/^8|^(?=1[18])..(\d{3})*$/.test(n)?'an':'a' (es6fiddle.net/iiehl1ex). It is 46 bytes long. \$\endgroup\$ Dec 20, 2015 at 12:06
  • 2
    \$\begingroup\$ @ScottKaye The code is very simple: It checks if it starts with 8, if it starts with 1[18] and if the length of numbers is 2 * (3n). Basically, it is your entire code, but within a regular expression. \$\endgroup\$ Dec 20, 2015 at 15:13
2
\$\begingroup\$

Seriously, 43 40 bytes

9⌐9τk,;;$l3@\3*╤@\(íub)$#p'8=)XkΣ'n*'a+

The strategy here is to only look at the 1, 2, or 3 most significant digits, by integer-dividing the input by the largest value 10^(3n) that is less than the input.

Try it online

Explanation:

9⌐9τk,;;$l3@\3*╤@\(íub)$#p'8=)XkΣ'n*'a+
9⌐9τk                                    push [11, 18]
     ,;;                                 push 3 copies of input (n)
        $l                               get length of n as string (effectively floor(log(n,10)))
          3@\3*╤                         get largest 10^(3n) less than the length
                @\                       get most significant digits of n (x)
                  (í                     bring list from back, push the index of x in the list or -1 if not in list
                    ub)                  increment by 1, convert to boolean, shove to bottom
                       $#p               push first digit from n (as string)
                          '8=            push 1 if "8" else 0
                             )X          shove to bottom of stack, discard remaining digits
                               kΣ'n*     push sum of stack, push a string containing that many "n"s
                                    'a+  push "a", concatenate
\$\endgroup\$
2
\$\begingroup\$

Retina, 34

Direct translation of my sed answer:

+`(.)...$
$1
^8.*|^1[18]$
an
\d+
a

Try it online.

One byte saved thanks to @Timwi.

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

Perl 6,  31  30 bytes

{'a'~'n'x?/^8|^1<[18]>[...]*$/} # 31 bytes
{<a an>[?/^8|^1<[18]>[...]*$/]} # 31 bytes
{<a an>[?/^8|^[11|18][...]*$/]} # 31 bytes

{'a'~'n'x?/^8|^1[1|8][...]*$/} # 30 bytes
{<a an>[?/^8|^1[1|8][...]*$/]} # 30 bytes

( Perl 6 uses [ ] in regexes for non-capturing ( ), and uses <[ ]> for character sets )

Usage:

# store it in a lexical code variable for ease of use
my &code = {...}

my @a  = <0 110 1111 1863 110000 180000 1141592 1897932>;
my @an = <8 11 18 843 8192 11000 18000 11234567 18675309>;

say @a.map: &code;
say @an.map: &code;
(a a a a a a a a)
(an an an an an an an an an)
\$\endgroup\$
2
\$\begingroup\$

PostScript, 109 bytes

(a)exch 10 string cvs dup[exch length 3 mod 2 eq{(11)(18)}if(8)]{anchorsearch{pop pop(an)exch}if}forall pop =

The code verifies if the number starts with certain prefixes. The prefix 8 is always checked (eight, eighty-something, eight-hundreds-and), but 11 and 18 (eleven and eighteen) are checked only when the number of digits is a multiple of 3 plus 2.

We start with a tentative result of a and when a prefix is found the result gets replaced with an. anchorsearch is used to avoid extracting a prefix from the string. Even if a match is found we continue verifying the rest of the prefixes – why waste 5 bytes for the  exit? –, but because the original string gets replaced with a we are sure not to get any false positives.

To return the a-or-an result on the operand stack instead of printing it, remove the trailing  = (resulting length: 107 bytes).

Test code:

/DO {
    ... the code above ...
} def

(Should be "a"s:)  = {0 110 1111 1863 110000 180000 1141592 1897932}     { DO } forall
(Should be "an"s:) = {8 11 18 84 843 8192 11000 18000 11234567 18675309} { DO } forall
flush
\$\endgroup\$
2
\$\begingroup\$

PostScript (with binary tokens), 63 bytes

(a)’>10’¥’1’8[’>’b3’j2’={(11)(18)}if(8)]{’${’u’u(an)’>}if}’I’u=

The are bytes with the value 146 (decimal), ¥ is a 165 and $ is a 3. All others are printable 7-bit ASCII characters.

This is the same as my PostScript [pure ASCII] version, but uses binary tokens where this helps reduce the total length. I post it separately for 3 reasons:

  • In the general case, an implementation that minimizes the ASCII code is not necessarily the same as the one minimizing the binary version. Some longer piece of ASCII PostScript code could compress better than another and its corresponding binary version be shorter.
  • Binary code is not suitable everywhere, so a pure ASCII answer may be preferred even if longer.
  • It wouldn’t be fair to compare the length of a pure ASCII PostScript answer with one using binary encodings.
\$\endgroup\$
1
\$\begingroup\$

Pyth, 29 31

?:_ec_z3"(^18$|^11$|^8)"0"an"\a

Reverses the string, splits it into sections of three, reverses it again, then chooses the appropriate ending.

\$\endgroup\$
2
  • 5
    \$\begingroup\$ This is wrong on the input 111 - it gives an \$\endgroup\$
    – isaacg
    Dec 17, 2015 at 5:41
  • \$\begingroup\$ You're right. Fixed. \$\endgroup\$
    – Moose
    Dec 17, 2015 at 22:00
1
\$\begingroup\$

Python 3, 110 93 91 76 74 70 65 64 bytes

Here is a long one, but a simple one.

Edit: Corrected with thanks to isaacg. Saved some whitespace after the comparisons. Many bytes saved thanks to Timwi, Mego, benpop and Alissa.

n=input();print("a"+"n"*(len(n)%3>1and n[:2]in"118"or"8"==n[0]))

or for the same number of bytes.

n=input();print("a"+"n"[:len(n)%3>1and n[:2]in"118"or"8"==n[0]])

Ungolfed:

def a():
    n=input()
    if "8"==n[:1]:
        a = "n"
    elif len(n)%3 == 2 and (n[:2] in ["11", "18"]):
        a = "n"
    else:
        a = ""
    return "a"+a
\$\endgroup\$
8
  • \$\begingroup\$ This is incorrect on the input 843, "eight hundred fourty-three", which should be an. \$\endgroup\$
    – isaacg
    Dec 17, 2015 at 5:51
  • \$\begingroup\$ @isaacg Not only are you correct, but this simplifies my code immensely. Thanks! It turns out I was only looking at eight, eight thousand, eight million, while ignoring cases like eighty and eight hundred. \$\endgroup\$
    – Sherlock9
    Dec 17, 2015 at 5:54
  • \$\begingroup\$ Why (-~len(n)%3)<1 instead of len(n)%3==2? \$\endgroup\$
    – Timwi
    Dec 17, 2015 at 5:59
  • \$\begingroup\$ Could (n[:2]=="11"or n[:2]=="18") be shortened to "118".contains(n[:2])? \$\endgroup\$
    – Timwi
    Dec 17, 2015 at 6:00
  • \$\begingroup\$ Or even n[:2]in"118"? \$\endgroup\$
    – Timwi
    Dec 17, 2015 at 6:03
1
\$\begingroup\$

Java 10, 102 bytes

n->{var N=n+"";return(n>9&&"118".contains(N.substring(0,2))&N.length()%3>1)|N.charAt(0)==56?"an":"a";}

Try it online.

Explanation:

n->{                  // Method with integer parameter and String return-type
  var N=n+"";         //  Input integer as String
  return(n>9&&        //  If the input has at least two digits,
    "118".contains(N.substring(0,2))
                      //  and the first two digits are "11" or "18",
    &N.length()%3>1)  //  and the length modulo-3 is 2
   |N.charAt(0)==56?  //  Or if the first digit is an '8':
     "an"             //   Return "an"
   :                  //  Else:
     "a";}            //   Return "a"
\$\endgroup\$
1
\$\begingroup\$

Japt, 28 27 bytes

'a+'npUì v ¥8ª[B18]d¥UìA³ v

Try it online!

Unpacked & How it works

'a+'npUì v ==8||[B18]d==UìAp3  v

'a+'np  "a" + "n".repeat(...)
Uì v ==8    First digit in decimal == 8
||          or...
[B18]d      [11,18].some(...)
==UìAp3  v  == First digit in base 10**3
\$\endgroup\$
1
  • \$\begingroup\$ You can replace 1e3 with \$\endgroup\$
    – Oliver
    May 24, 2018 at 0:45
1
\$\begingroup\$

GNU sed -r + BSD number, 34 bytes

s/(e?).*/number &/e
s//a\1/
y/e/n/

First we convert to English number. Then delete everything except a possible initial e, and prefix with a. Then convert the e (if present) to n. The only golfing trick is to match the optional e in the first substitution, so we can reuse the pattern in the following line.

Demo

for i in 0 8 11 18 84 110 843 1111 1863 8192 \
    11000 18000 110000 180000 1141592 1897932 11234567 18675309
do printf "%'10d → %s\n" $i $(./66841.sed <<<$i)
done
         0 → a
         8 → an
        11 → an
        18 → an
        84 → an
       110 → a
       843 → an
     1,111 → a
     1,863 → a
     8,192 → an
    11,000 → an
    18,000 → an
   110,000 → a
   180,000 → a
 1,141,592 → a
 1,897,932 → a
11,234,567 → an
18,675,309 → an
\$\endgroup\$
1
\$\begingroup\$

Perl 5 -p, 26 bytes

$_=a.n x/^8|^1[18](...)*$/

Try it online!

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

05AB1E, 26 21 bytes

₄внŽ4$2ôQàI8Å?+>„ans∍

Try it online or verify all test cases.

Explanation:

₄в          # Convert the (implicit) input-integer to base-1000 as list
  н         # Pop and push the first integer in this list
            #  i.e. 11234567 → [11,234,567] → 11
            #  i.e. 110000 → [110,0] → 110
            #  i.e. 8192 → [8,192] → 8
   Ž4$      # Push compressed integer 1118
      2ô    # Split it into parts of size 2: [11,18]
        Q   # Check for each if it's equal to the integer
         à  # Pop and push the maximum to check if either is truthy
            #  11 → [1,0] → 1
            #  110 → [0,0] → 0
            #  8 → [0,0] → 0
I           # Push the input-integer again
 8Å?        # Check if it starts with an 8
            #  11234567 → 0
            #  110000 → 0
            #  8192 → 1
+           # Add the two checks together
 >          # Increase it by 1
            #  1+0 → 1 → 2
            #  0+0 → 0 → 1
            #  0+1 → 1 → 2
  „an       # Push string "an"
     s∍     # And shorten it to a length equal to the integer
            #  2 → "an"
            #  1 → "a"
            #  2 → "an"
            # (after which the result is output implicitly)

See this 05AB1E tip of mine (section How to compress large integers?) to understand why Ž4$ is 1118.

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

TeaScript, 35 bytes

[18,11,8,80]I(+xh(x.n%3¶3©?'an':'a'

Try it here.

Explanation

               xh(x.n%3¶3           get the relevant digits from the input
                                    xh compiles to x.head which returns the
                                    first n chars of x (implicit input)
                                    ¶ ('\xb6') compiles to ||
              +                     cast the result to an integer since
                                    .includes does a strict comparison
                         ©          ('\xa9') compiles to ))
[18,11,8,80]                        array of the special cases
            I(                      I( is an alias for .includes( which
                                    returns true if the array contains the
                                    argument
                          ?'an':'a' finally, return 'an' if the array
                                    contains the number, 'a' otherwise
\$\endgroup\$
0
\$\begingroup\$

Python 2.7, 66

s=`input()`
print['a','an'][s[:1]=='8'or s[:2]in len(s)%3/2*'118']

Obviously not as short as the lambda one.

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

QuadS, 32 bytes

'a',⍵
^8.*|^1[18](...)*$
⊃⍵L/'n'

Try it online!

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

Stax, 25 bytes

â-x▬♪°∞▄'δL|÷æ╪║>₧4¢ÿ·7åR

Run and debug it

Unpacked, ungolfed, and commented, it looks like this.

Vk|Eh       get the first "digit" after converting to base 1000
AJ|Eh       get the first "digit" after converting to base 100
c20>9*^/    if the result is greater than 20, divide it by 10 again
"AMj"!#     is the result one of [8, 11, 18]?
^           increment by 1
.an(        keep that many characters of the string "an"

Run this one

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

Whitespace, 243 bytes

[S S S T    T   S S S S T   N
_Push_97_a][T   N
S S _Print_as_character][S S S T    N
_Push_1][S N
S _Duplicate_1][S N
S _Duplicate_1][T   N
T   T   _Read_STDIN_as_integer][T   T   T   _Retrieve_input][N
S S S T N
_Create_Label_LOOP][S N
T   _Swap_top_two][S S S T  N
_Push_1][T  S S S _Add][S N
T   _Swap_top_two][S N
S _Duplicate][S S S T   T   S S T   S S N
_Push_100][T    S S T   _Subtract][N
T   T   T   N
_If_negative_jump_to_Label_TWO_DIGITS][S S S T  S ST    S N
_Push_10][T S T S _Integer_division][N
S N
S T N
_Jump_to_Label_LOOP][N
S S T   N
_Create_Label_TWO_DIGITS][S N
S _Duplicate][S S S T   S S S N
_Push_8][T  S S T   _Subtract][N
T   S S S N
_If_zero_jump_to_Label_PRINT_n][S N
S _Duplicate][S S S T   S T T   N
_Push_11][T S S T   _Subtract][N
T   S S N
_If_0_jump_to_Label_2_MOD_3][S N
S _Duplicate][S S S T   S S T   S N
_Push_18][T S S T   _Subtract][N
T   S S N
_If_0_jump_to_Label_2_MOD_3][S S S T    S ST    S N
_Push_10][T S T S _Integer_division][S N
S _Duplicate][N
T   S N
_If_0_jump_to_Label_EXIT][N
S N
T   N
_Jump_to_Label_TWO_DIGITS][N
S S S N
_Create_Label_2_MOD_3][S N
T   _Swap_top_two][S S S T  T   N
_Push_3][T  S T T   _Modulo][S S S T    S M
_Push_2][T  S S T   _Subtract][N
T   T   N
_If_negative_jump_to_Label_EXIT][N
S S S S N
_Create_Label_PRINT_n][S S S T  T   S T T   T   S N
_Push_110_n][T  N
S S _Print_as_character][N
S S N
_Create_Label_EXIT]

Letters S (space), T (tab), and N (new-line) added as highlighting only.
[..._some_action] added as explanation only.

Try it online (with raw spaces, tabs and new-lines only).
Program stops with an error: No exit found.

Explanation in pseudo-code:

Print "a"
Integer input = STDIN as integer
Integer counter = 1
Start LOOP:
  counter = counter + 1
  If(input < 100)
    Jump to function TWO_DIGITS
  input = input integer-divided by 10
  Go to next iteration of LOOP

function TWO_DIGITS:
  If(input == 8)
    Jump to function PRINT_n
  If(input == 11 or input == 18)
    Jump to function 2_MOD_3
  input = input integer-divided by 10
  If(input == 0)
    Exit program
  Recursive call to TWO_DIGITS

function 2_MOD_3:
  If(counter modulo-3 != 2)
    Exit program
  Jump to function PRINT_n

function PRINT_n:
  Print "n"
  Exit program
\$\endgroup\$
0
\$\begingroup\$

C++, 80 79 bytes

[](int i){for(;i>999;i/=1e3);return i-11&&i-18&&i/100-8&&i/10-8&&i-8?"a":"an";}

It turned out 4 bytes shorter to explicitly test against 8xx and 8x than to have another /=10 loop, like this:

[](int i){for(;i>999;i/=1e3);for(i==11|i==18?i=8:0;i>9;i/=10);return i-8?"a":"an";}

Demo

#include <locale>
#include <cstdio>
int main(int argc, char**argv)
{
    auto const f =
        [](int i){for(;i>999;i/=1e3);return i-11&&i-18&&i/100-8&&i/10-8&&i-8?"a":"an";}
    ;

    std::locale::global(std::locale{""});
    for (int i = 1;  i < argc;  ++i) {
        auto const n = std::stoi(argv[i]);
        printf("%'10d → %s\n", n, f(n));
    }
}
         0 → a
         8 → an
        11 → an
        18 → an
        84 → an
       110 → a
       843 → an
     1,111 → a
     1,863 → a
     8,192 → an
    11,000 → an
    18,000 → an
   110,000 → a
   180,000 → a
 1,141,592 → a
 1,897,932 → a
11,234,567 → an
18,675,309 → an
\$\endgroup\$
4
  • \$\begingroup\$ I don't know C++ too well, but can i/=1000 be i/=1e3, and can all && become &? \$\endgroup\$ May 25, 2018 at 8:53
  • \$\begingroup\$ It indeed seems to work: Try it online. \$\endgroup\$ May 25, 2018 at 9:02
  • 1
    \$\begingroup\$ @Kevin - I did at one point have 1e3 there; I changed it during debugging and forgot to change it back. The && can't all be &, because subtraction yields integers and not booleans - e.g. 19-11 is 8, and 19-18 is 1; see that 8 && 1 is true, but 8 & 1 is false. We could use & but we'd need to change - to != and also add parentheses. \$\endgroup\$ May 25, 2018 at 10:01
  • \$\begingroup\$ Ah of course.. & indeed doesn't work here, my bad. Btw, why don't you add a TIO-link to your answer as well? \$\endgroup\$ May 25, 2018 at 11:36
-1
\$\begingroup\$

Perl, 71 55 49 bytes

$_=<>;$_=/^8/||/^1[18]/&&length%3==1?'an':'a';say

I knew the ternary operator would help one day...

Let me break this down.

  • $_=<> accepts a number as input.
  • The big $_=... block will set the value of $_ after it's used.
    • ...?...:... is the ternary operator. If the condition (first argument) is true, it returns the second argument. Otherwise, it returns the third.
    • /^8/||(/^1[18]/&&length%3==2) checks to see if the number starts with 8 or begins with 11 or 18 (1[18] accepts either) and has a length mod 3 of 2.
    • If that's true, $_ is set to an. Otherwise, it's set to a.
  • It then prints out the contents of $_ (either a or an) with say.

Changes

  • Saved 16 bytes thanks to msh210.
  • Saved 6 bytes by removing parens and using defaults.
\$\endgroup\$
6
  • \$\begingroup\$ $_=<>;$_=(/^8/)||/^1[18]/&&length($_)%3==1?'an':'a';say saves a few bytes. (Though the number to compare to it depends on what your newline character is, but that doesn't change the byte count.) \$\endgroup\$
    – msh210
    Dec 17, 2015 at 18:58
  • \$\begingroup\$ @msh210 It looks like that's only 55 bytes meaning it saves 16 bytes. I'll add that in. Thanks! \$\endgroup\$ Dec 17, 2015 at 19:14
  • \$\begingroup\$ You're welcome. Oh, and you can drop the first parens (I assume. I haven't tested). I'd think you could also change length($_) to length (or at least drop the parens) but that's not working for me for some reason. \$\endgroup\$
    – msh210
    Dec 17, 2015 at 19:20
  • \$\begingroup\$ @msh210 Yep, you can drop the parens and ($_) to get $_=<>;$_=/^8/||/^1[18]/&&length%3==1?'an':'a';say, which is only 49 bytes. \$\endgroup\$ Dec 17, 2015 at 19:23
  • \$\begingroup\$ You can save some bytes by using -p instead of $_=<> and say, y///c instead of length, and dropping the quotes around a and an: perl -pe'$_=/^8/||/^1[18]/&&y///c%3==2?an:a' (34 bytes + 1 for -p). Note that input cannot end with a newline: echo -n 11 | perl -pe'...'. This also fixes a bug: length%3==2 is parsed as length(%3)==2, not as length($_)%3==2, so it always returns false. \$\endgroup\$ Dec 17, 2015 at 21:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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