Ain't no thang! ...or is it?

24

Introduction

Street talk can be really difficult to understand, in particular to programmers, who aren't known to be very streetwise.

It is your job to create an interpreter to help us all survive in the urban environment.

Challenge

Given an English sentence as input, create a program or a function that determines whether the outcome of the sentence is positive or negative.

The sentence will contain 0 to 2 negative words. As any programmer knows, a double negative results in a positive. Therefore, your code must output or return a truthy/falsey value according to the following rule:

No negative words  -> truthy
One negative word  -> falsey
Two negative words -> truthy

The list of negative words:

  • no, not, none
  • Anything ending in n't
  • never, neither, nor
  • nobody, nothing, nowhere

There is one edge case. Whenever a sentence begins with No,, that word isn't treated as a negative word when determining the result (it does count towards the number of negative words so there can be just one more).

The sentence will follow basic grammar rules (capitalization, punctuation) and will only contain words that can be found from a dictionary (luckily, this doesn't invalidate the question title). The sentence won't contain any proper nouns (sorry, Dr. No, you're out).

Test cases

Truthy:

Yes.
It's noon.
Hello, World!
What is this?
Ain't no thang!
Never say never.
No, it's noon now.
Neither me nor you.
I didn't do nothing!
No, I am your father.
A non-alcoholic drink.
I can't get no satisfaction.
All your base are belong to us.

Falsey:

No.
No, no!
Not today.
Neither am I.
Don't do that!
That's no moon!
And none survived.
Is this not my car?
No man is an island.
Nosebleeds are no fun.
Nothing compares to you.
That's a no, I'm afraid.
No, I am not your mother.

The irony here, of course, is that some of these should be interpreted differently. But hey, you can't fault the speaker for not conforming to our logic.

Rules

Standard loopholes are forbidden. This is , so be concise!

Antti29

Posted 2017-11-21T07:07:26.073

Reputation: 939

1Nobody ain't never been neither nowhere nor nothing. – Magic Octopus Urn – 2017-11-22T19:01:52.117

1@MagicOctopusUrn: You can lose the been for a 100% negative sentence! – Antti29 – 2017-11-22T19:36:54.190

Answers

10

Retina, 63 bytes

No,

Mi`\bn(e(ith|v)er|o(|body|ne|r|t|thing|where))\b|n't\b
0|2

Try it online!

Explanation

No,

Remove No, from the input. Due to the capitalisation rules, this can only appear at the beginning of the input, so we don't need an explicit ^.

Mi`\bn(e(ith|v)er|o(|body|ne|r|t|thing|where))\b|n't\b

Count the number of matches of the case-insensitive regex after the `. It just matches all the relevant words, where I've extracted common prefixes/suffixes with the alternatives.

0|2

Count 0 or 2s, so we turn even counts into 1 and odd counts into 0.

Martin Ender

Posted 2017-11-21T07:07:26.073

Reputation: 184 808

do you do the common letter extraction by hand or use a program that finds the optimal solution for you? – Jonah – 2017-11-21T14:02:41.013

@Jonah I did that by hand. There are tools for automated regex metagolf, but they usually take two lists, one to match and one to fail, and generate a regex for that. I'm not aware of any tool that generates an optimal regex to match a particular set of substrings in a larger string. – Martin Ender – 2017-11-21T14:08:23.833

3could make an interesting challenge... – Jonah – 2017-11-21T14:15:25.670

You should be able to assume that n't doesn't need \b after it, since the words have to come from a dictionary. Also, I had the same thing, but I didn't have the meat of the answer as concisely, using a couple more bytes. – mbomb007 – 2017-11-21T16:50:43.373

8

Bash, 115 107 99 98 97 95 85 bytes

Uses packages Core Utilities (for wc) and grep. Assume the sentence is given via Standard Input. History expansion is disabled by set +o histexpand.

((~`grep -Pio "(?!^no,)\b(no(|t|r|ne|body|thing|where)|ne(v|ith)er|.*n't)\b"|wc -l`%2))

Check the result: In Bash 0 is for true, 1 is for false

How does it work?

((                       )) # Logical evaluation: non-zero to TRUE, zero to FALSE
  ~                    %2   # C-style arithmetic: Bit-Negate and Modulus 2
   $(                 )     # Output of the program chain
     grep -Pio "regex"      # PCRE match, ignore case, output matching part one-per-line
     | wc -l                # Pipe to `wc` and count number of lines

18 bytes (115 to 99) saved by inspiration from Qwertiy's answer and Martin Ender's answer. 1 byte thanks to Nahuel Fouilleul.

iBug

Posted 2017-11-21T07:07:26.073

Reputation: 2 477

regex is not correct : matches noon and not That's a no, I'm afraid. – Nahuel Fouilleul – 2017-11-21T12:51:30.077

@NahuelFouilleul Fixed. – iBug – 2017-11-21T12:54:38.847

to check: tio however couldn't paste tests because comment length limit

– Nahuel Fouilleul – 2017-11-21T12:58:39.893

this gives right results ((~$(grep -Pio "(?!^no,)\b(no(|t|r|ne|body|thing|where)|ne(v|ith)er)\b|.*n't\b"|wc -l)%2)) – Nahuel Fouilleul – 2017-11-21T13:00:41.900

back quotes instead of $(..) save 1 byte – Nahuel Fouilleul – 2017-11-21T13:02:13.090

@NahuelFouilleul Sorry for a parenthesis misplacement. Check my current answer. – iBug – 2017-11-21T13:02:19.963

71 bytes : here

– Nahuel Fouilleul – 2017-11-21T13:08:11.230

@NahuelFouilleul I'm afraid the neil brothers will give a wrong result, as well as never. – iBug – 2017-11-21T13:13:08.220

5

Javascript ES6, 89 87 86 chars

s=>s.match(/(?!^no,)\bn(o(|t|r|ne|body|thing|where)|e(v|ith)er)\b|n't\b|$/ig).length&1

Test:

f=s=>s.match(/(?!^no,)\bn(o(|t|r|ne|body|thing|where)|e(v|ith)er)\b|n't\b|$/ig).length&1

console.log(`Yes.
It's noon.
Hello, World!
Never say never.
Ain't no thang!
No, it's noon now.
Neither me nor you.
I didn't do nothing!
No, I am your father.
A non-alcoholic drink.
I can't get no satisfaction.
All your base are belong to us.`.split`
`.every(f))

console.log(`No.
No, no!
Not today.
Neither am I.
Don't do that!
That's no moon!
And none survived.
No man is an island.
Nosebleeds are no fun.
Nothing compares to you.
That's a no, I'm afraid.
No, I am not your mother.`.split`
`.every(s=>!f(s)))

Qwertiy

Posted 2017-11-21T07:07:26.073

Reputation: 2 697

2

Perl 5, 74 bytes

73 bytes code + 1 for -p.

s/No,//;$_=!(s/(\bn(o(r|t|ne|body|thing|where)?|e(v|ith)er)|n't)\b//gi%2)

Try it online!

Dom Hastings

Posted 2017-11-21T07:07:26.073

Reputation: 16 415