You're on a 8 day streak!

82

12

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

Luke

Posted 2015-12-17T04:50:28.540

Reputation: 5 091

1I thought this would be really easy... then I noticed 218 uses the 8's letter, not the 2's. – Cyoce – 2015-12-17T05:07:17.303

31Is this endorsed by Duolingo? If not, you should get them to pay us for improving the language on a language learning site. – Arc676 – 2015-12-17T07:38:52.137

10Is 1100 (an) eleven hundred or (a) one thousand and one hundred? – user3819867 – 2015-12-17T08:04:01.497

11Bilbo would disagree with some of your test cases. :) – Martin Ender – 2015-12-17T09:26:02.927

@user3819867: Wasn't it, it has to be the word's first non silent letter the article is refering to is an a, e, i, o or u so it is an "an"? This confuses me, why it isn't in that case an one thousand and one hundred? is the "one" not considered to be part of the number? would be strange in my eyes too. Or did I just miss something when we had this topic in school? :x – Zaibis – 2015-12-17T09:56:10.187

9@Zaibis: "one" here is pronounced like "wun", which has a consonant sound. Hence, "a one thousand and one hundred day streak". – El'endia Starman – 2015-12-17T10:26:46.373

@El'endiaStarman: oh yeah.... you are right. I never realized that. I had to speak out loud 3 times "one" and "wun" to notice that its actually some thing kinda close to w but definetly not an o.... really never realized. ok now its clear. – Zaibis – 2015-12-17T10:42:12.470

31They probably left this bug because they thought no one would reach a 8 day streak. – PNDA – 2015-12-17T11:36:19.050

@user3819867 Just like one thousand eight hundred, we're treating it as one thousand one hundred (see the eighth test case.) – Luke – 2015-12-17T13:17:19.230

Wow, that is just sad, an app for teaching language and it has a grammatical error. Good catch @Luke. – Ashwin Gupta – 2015-12-18T04:05:51.020

https://www.duolingo.com/comment/12400979. Okay, I posted on their forums, they may fix it if it gets noticed. – Ashwin Gupta – 2015-12-18T04:08:15.470

2@AshwinGupta Here you go! ♦ A hard earned lingot. – wizzwizz4 – 2015-12-18T14:23:15.103

6"Thus the only numbers preceded by an are those whose pronunciation begins with eight, eleven, eighteen, or eighty." You just made Undecillion cry... I hope you're happy – corsiKa – 2015-12-18T23:54:22.937

1@PandaLion98: an 8 day streak. – Jost – 2015-12-19T08:31:01.617

You do know how long we have to wait to get a 2,147,483,647-day streak, right? I'll be back in 5.8 million years to see how we're getting on... – Toby Speight – 2018-05-24T14:29:05.653

Answers

15

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.

isaacg

Posted 2015-12-17T04:50:28.540

Reputation: 39 268

3Very creative. Also, terrifying. – Hellreaver – 2015-12-20T12:50:49.087

29

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.

xnor

Posted 2015-12-17T04:50:28.540

Reputation: 115 687

I know this is a super old question, but I think `n`>='8' saves three bytes. – Lynn – 2018-05-24T18:01:10.490

@Lynn Won't that mess up nines though? – xnor – 2018-05-24T18:05:08.777

Oh, of course! I was fooled by the test suite :) – Lynn – 2018-05-24T18:48:15.787

12

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.

Digital Trauma

Posted 2015-12-17T04:50:28.540

Reputation: 64 644

11

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.

Digital Trauma

Posted 2015-12-17T04:50:28.540

Reputation: 64 644

2+1 for using bsd-games, I didn't actually think they'd ever be useful :) – ASCIIThenANSI – 2015-12-17T15:38:35.800

@ASCIIThenANSI yep, bsd-games are useful here and there :)

– Digital Trauma – 2015-12-17T17:28:17.480

9

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

Martin Ender

Posted 2015-12-17T04:50:28.540

Reputation: 184 808

1+1 this is almost the same level of abuse of regex as primality testing :) – cat – 2015-12-17T12:07:14.313

1And 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? – ceased to turn counterclockwis – 2015-12-18T18:22:45.930

1@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. – Martin Ender – 2015-12-18T18:24:32.580

6

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";}

Luke

Posted 2015-12-17T04:50:28.540

Reputation: 5 091

4

Mathematica, 53 bytes

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

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

LegionMammal978

Posted 2015-12-17T04:50:28.540

Reputation: 15 731

3

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/

Scott

Posted 2015-12-17T04:50:28.540

Reputation: 217

I'm missing the reason for eval – Daniel F – 2015-12-18T01:26:44.643

eval returns the result of the last expression. I could use a multi-line function and an explicit return, but that's 1 more byte unfortunately. – Scott – 2015-12-18T02:31:23.770

1that makes sense. Thanks for explaining. – Daniel F – 2015-12-18T02:54:21.400

It can be done in 64 bytes if you make the function body a single expression via comma operator: n=>(s=''+n,s[0]==8||(/^11|^18/.test(s)&&s.length%3==2)?'an':'a') – Pavlo – 2015-12-19T15:37:49.283

Also, a byte can be shaved of the regex: /^1[18]/ – Pavlo – 2015-12-19T15:47:12.820

1@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! – Scott – 2015-12-19T20:43:50.237

@ScottKaye also, since && has higher precedence than || you can skip inner parenthesis: n=>(s=''+n,s[0]==8||/^1[18]/.test(s)&&s.length%3==2?'an':'a')

– Pavlo – 2015-12-19T21:54:09.317

1@Pavlo Sweet, updated again! Learning lots, thank you very much :) – Scott – 2015-12-20T02:56:12.357

1

Can't you just do n=>(n+='')[0]==8||/^1[18]/.test(n)&&n.length%3==2?'an':'a'? It seems to work. (Test: http://www.es6fiddle.net/iie8gis6/)

– Ismael Miguel – 2015-12-20T07:47:06.993

How about n=>/^8|^1[18]/.test(n+='')&&n.length%3==2?'an':'a'? Seems to work fine. Try it on http://www.es6fiddle.net/iie8oauz/. The code is 50 bytes long. – Ismael Miguel – 2015-12-20T07:55:57.303

@IsmaelMiguel the first one works (good job!), the second one fails: http://www.es6fiddle.net/iiegbo0r/ (because if the number start with 8 it must be 'an', no matter the length).

– Pavlo – 2015-12-20T11:34:02.983

@Pavlo Here is a new version, that passes all the tests: n=>/^8|^(?=1[18])..((\d){3})*$/.test(n)?'an':'a' (http://www.es6fiddle.net/iiehbub9/). It has -2 bytes (48 bytes total).

– Ismael Miguel – 2015-12-20T11:56:39.673

2

URGH! Forgot to shave 2 bytes! Here is the final one: n=>/^8|^(?=1[18])..(\d{3})*$/.test(n)?'an':'a' (http://www.es6fiddle.net/iiehl1ex/). It is 46 bytes long.

– Ismael Miguel – 2015-12-20T12:06:20.160

@IsmaelMiguel You guys are crazy, I don't even understand this code anymore, nice! Community wiki! – Scott – 2015-12-20T14:59:07.657

2@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. – Ismael Miguel – 2015-12-20T15:13:02.987

3

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

jdaw1

Posted 2015-12-17T04:50:28.540

Reputation: 131

2

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

Mego

Posted 2015-12-17T04:50:28.540

Reputation: 32 998

2

Retina, 34

Direct translation of my sed answer:

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

Try it online.

One byte saved thanks to @Timwi.

Digital Trauma

Posted 2015-12-17T04:50:28.540

Reputation: 64 644

2

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)

Brad Gilbert b2gills

Posted 2015-12-17T04:50:28.540

Reputation: 12 713

2

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

sags

Posted 2015-12-17T04:50:28.540

Reputation: 43

2

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.

sags

Posted 2015-12-17T04:50:28.540

Reputation: 43

1

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

Bubbler

Posted 2015-12-17T04:50:28.540

Reputation: 16 616

You can replace 1e3 with – Oliver – 2018-05-24T00:45:47.927

1

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"

Kevin Cruijssen

Posted 2015-12-17T04:50:28.540

Reputation: 67 575

1

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

Toby Speight

Posted 2015-12-17T04:50:28.540

Reputation: 5 058

1

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

Sherlock9

Posted 2015-12-17T04:50:28.540

Reputation: 11 664

This is incorrect on the input 843, "eight hundred fourty-three", which should be an. – isaacg – 2015-12-17T05:51:23.607

@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. – Sherlock9 – 2015-12-17T05:54:56.190

Why (-~len(n)%3)<1 instead of len(n)%3==2? – Timwi – 2015-12-17T05:59:46.330

Could (n[:2]=="11"or n[:2]=="18") be shortened to "118".contains(n[:2])? – Timwi – 2015-12-17T06:00:47.237

Or even n[:2]in"118"? – Timwi – 2015-12-17T06:03:14.680

Why not wrap the statement with print()? n=input();print("an"if"8"==n[:1]or len(n)%3==2and n[:2]in"118"else"a") – benpop – 2015-12-17T06:28:24.953

You can shorten "8"==n[:1] to "8"==n[0] also you can use "a"+"n"*(conditions) instead of "an"if conditions else "a". And then you can shorten conditions by one more space by moving "8"==n[0] to the end. So the final version will be n=input();print("a"+"n"*(len(n)%3==2and n[:2]in"118"or"8"==n[0])) – Alissa – 2015-12-17T14:20:14.377

@Alissa Thanks! Added another version with the same byte count so I didn't copy you completely – Sherlock9 – 2015-12-17T15:18:42.320

0

05AB1E, 26 bytes

g3%ô¬D11Qs18Q+I1£8Q+>„ans∍

Can probably be golfed a bit more, but its working.

Try it online or verify all test cases.

Explanation:

g3%                  # Length of the input, modulo-3
                     #  11234567 → 8 → 2
                     #  110000 → 6 → 0
   ô                 # Split the input into chunks of that size
                     #  11234567 and 2 → ['11', '23', '45', '67']
                     #  110000 and 0 → ['110000']
    ¬                # Take the Head (first element)
                     #  ['11', '23', '45', '67'] → '11'
                     #  ['110000'] → '110000'
     D11Q            # Does it equal 11?
                     #  '11' and 11 → 1
                     #  '110000' and 11 → 0
     s18Q            # Or does it equal 18?
                     #  '11' and 18 → 0
                     #  '110000' and 18 → 0
         +           # Add them together (if it was either 11 or 18, this becomes 1)
                     #  1 and 0 → 1
                     #  0 and 0 → 0
I1£                  # Get the first character of the input
                     #  11234567 → '1'
                     #  110000 → '1'
   8Q                # Does it equal 8?
                     #  '1' and 8 → 0
          +          # Add them together
                     #  1 and 0 → 1
                     #  0 and 0 → 0
           >         # Increase it by 1
                     #  1 → 2
                     #  0 → 1
            „ans∍    # Push "an", and shorten it to a size equal to the result above
                     #  "an" and 2 → "an"
                     #  "an" and 1 → "a"

Kevin Cruijssen

Posted 2015-12-17T04:50:28.540

Reputation: 67 575

0

QuadS, 32 bytes

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

Try it online!

Uriel

Posted 2015-12-17T04:50:28.540

Reputation: 11 708

0

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

recursive

Posted 2015-12-17T04:50:28.540

Reputation: 8 616

0

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

Toby Speight

Posted 2015-12-17T04:50:28.540

Reputation: 5 058

I don't know C++ too well, but can i/=1000 be i/=1e3, and can all && become &? – Kevin Cruijssen – 2018-05-25T08:53:51.813

It indeed seems to work: Try it online.

– Kevin Cruijssen – 2018-05-25T09:02:36.750

1@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. – Toby Speight – 2018-05-25T10:01:57.470

Ah of course.. & indeed doesn't work here, my bad. Btw, why don't you add a TIO-link to your answer as well? – Kevin Cruijssen – 2018-05-25T11:36:18.157

0

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

Kevin Cruijssen

Posted 2015-12-17T04:50:28.540

Reputation: 67 575

0

Perl 5 -p, 26 bytes

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

Try it online!

Xcali

Posted 2015-12-17T04:50:28.540

Reputation: 7 671

0

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

Dom Hastings

Posted 2015-12-17T04:50:28.540

Reputation: 16 415

0

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.

janrn

Posted 2015-12-17T04:50:28.540

Reputation: 51

-1

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.

Moose

Posted 2015-12-17T04:50:28.540

Reputation: 883

5This is wrong on the input 111 - it gives an – isaacg – 2015-12-17T05:41:57.107

You're right. Fixed. – Moose – 2015-12-17T22:00:04.863

-1

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.

ASCIIThenANSI

Posted 2015-12-17T04:50:28.540

Reputation: 1 935

$_=<>;$_=(/^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.) – msh210 – 2015-12-17T18:58:10.593

@msh210 It looks like that's only 55 bytes meaning it saves 16 bytes. I'll add that in. Thanks! – ASCIIThenANSI – 2015-12-17T19:14:03.207

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. – msh210 – 2015-12-17T19:20:15.280

@msh210 Yep, you can drop the parens and ($_) to get $_=<>;$_=/^8/||/^1[18]/&&length%3==1?'an':'a';say, which is only 49 bytes. – ASCIIThenANSI – 2015-12-17T19:23:28.360

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. – ThisSuitIsBlackNot – 2015-12-17T21:01:38.150

$ perl -nE 'say/^(8|1[18](...)*$)/?an:a' 27+1 – Brad Gilbert b2gills – 2015-12-19T17:14:18.747