53
\$\begingroup\$

Some versions of the standard Android calculator app allow you to press a key, like 'sin' and then the 'del' key to make it 'si'. Probably just a bug which they can't be bothered with removing.

Screenshot of Android calculator

The following letters/letter groupings are typable:

 

sin
si
s
cos
co
c
tan
ta
t
ln
l
log
lo
e

So, 'tasteless' is typable, because ta-s-t-e-l-e-s-s and so is 'clogs' because 'c-log-s'. However 'got' is not typable, neither is 'an' or 'xyz'.

Write a program that takes a single word (or sequence of letters, only a-z in input) as input and produces output to indicate if a word is typable or not.

Output may be a single character/letter/digit/etc. or it may be larger. All typable words should produce the same output. All non-typable words should also produce the same output.

P.S. Just out of curiosity, is 'tasteless' the longest dictionary word that is typable?

\$\endgroup\$
7
  • 1
    \$\begingroup\$ Just to be clear: We output a single, consistent digit or letter of our choice to indicate the word is typeable, and a different, single, consistent digit or letter of our choice to indicate the word is not typeable. Is that correct? \$\endgroup\$
    – Dennis
    Dec 4, 2016 at 16:36
  • 3
    \$\begingroup\$ The longest word I could find is 10 letters, coelostats. \$\endgroup\$ Dec 4, 2016 at 21:48
  • 1
    \$\begingroup\$ So, is "πe" not a word? \$\endgroup\$
    – Mr Lister
    Dec 5, 2016 at 9:15
  • \$\begingroup\$ @Dennis Ya, more or less. \$\endgroup\$ Dec 5, 2016 at 14:43
  • 1
    \$\begingroup\$ Another 11: sissinesses \$\endgroup\$
    – Chris H
    Dec 6, 2016 at 13:29

15 Answers 15

21
+50
\$\begingroup\$

Perl, 47 43 41 + 1 = 42 bytes

-4 bytes thanks to @Sunny Pun. -2 bytes thanks to @Brad Gilbert b2gills and @Downgoat

Run with the -n flag.

say!/^(s(in?)?|co?|t(an?)?|ln?|log?|e)+$/

It can definitely be golfed futher, but in the spirit of competition, I'm leaving the mostly-original regex I came up with at the beginning. Returns nothing if true, 1 if false.

Try it online!

I downloaded a dictionary file, and the longest word I found was 11 letters -- tattletales

\$\endgroup\$
6
  • 1
    \$\begingroup\$ I think co? should be sufficient since sin will take care of the s :) Nice playing with /log?/! \$\endgroup\$
    – Sunny Pun
    Dec 4, 2016 at 16:49
  • 1
    \$\begingroup\$ It doesn't say that it needs to be case insensitive (remove i), or that the single result has to make sense (remove one !) \$\endgroup\$ Dec 4, 2016 at 18:19
  • \$\begingroup\$ Do you need i flag at very end since input is only a-z? \$\endgroup\$
    – Downgoat
    Dec 5, 2016 at 15:04
  • \$\begingroup\$ You can make this simpler: -2: /^(s|sin?|co?|t|tan?|ln?|log?|e)+$/ or if you prefer: /^([celst]|sin?|co|tan?|ln|log?)+$/ \$\endgroup\$ Dec 5, 2016 at 22:49
  • 1
    \$\begingroup\$ Enjoy your bounty! \$\endgroup\$ Dec 13, 2016 at 17:03
17
\$\begingroup\$

JavaScript (ES6), 44 bytes

s=>/^((si|ta|l)n?|co?|log?|[ste])+$/.test(s)

I think this is the shortest regex possible, but of course I may be wrong.

Explanation

An obvious first place to start would be a regex that simply includes all options separately:

s=>/^(sin|si|s|cos|co|c|tan|ta|t|log|lo|l|ln|e)+$/.test(s)

First, we can observe that cos can be formed from co and s, making it unnecessary:

s=>/^(sin|si|s|co|c|tan|ta|t|log|lo|l|ln|e)+$/.test(s)

Some of these, such as sin and si, can be combined by making the last letter optional:

s=>/^(sin?|s|co?|tan?|t|log?|ln?|e)+$/.test(s)

This works because sin? matches si with or without an n on the end, thus covering both sin and si.

There seems to be a lot of n?s as well. What if we put them all together?

s=>/^((si|ta|l)n?|s|co?|t|log?|e)+$/.test(s)

One more way to golf this would be to combine the remaining single-char options into a character range, but this comes out at the same length:

s=>/^((si|ta|l)n?|co?|log?|[ste])+$/.test(s)

And that's how you golf a simple regex. I believe this is the shortest possible regex that matches every string correctly, but perhaps not. I will award a +100 bounty to anyone who manages to improve upon this regex.

\$\endgroup\$
12
  • \$\begingroup\$ The one that I used in my answer is sort of similar: ^(s|t|co?|(l|ta|si)n?|log?|e)+$ \$\endgroup\$ Dec 4, 2016 at 21:23
  • \$\begingroup\$ Firstly, I count 44 bytes. Secondly, here's an alternate but longer solution: / /.test.bind(/^((si|ta|l)n?|co?|log?|[ste])+$/). \$\endgroup\$ Dec 4, 2016 at 21:41
  • \$\begingroup\$ @ConorO'Brien Whoops, don't know how I miscounted that... \$\endgroup\$ Dec 4, 2016 at 22:48
  • \$\begingroup\$ You can do a=/^((si|ta|l)n?|co?|log?|[ste])+$/;a.test for 42 bytes. I believe this is allowed, because a.test is an anonymous function. \$\endgroup\$ Dec 6, 2016 at 1:39
  • \$\begingroup\$ @SeeOneRhino It is, but it's not really allowable because f=a.test doesn't work. I wonder if it's acceptable to call with the alternate name a.test though... \$\endgroup\$ Dec 6, 2016 at 1:52
7
\$\begingroup\$

Pyth, 37 33 29 28 bytes

The code contains an unprintable character, so here is an xxd hexdump.

00000000: 737d 5173 4d5e 733c 5252 6336 2e22 6174  s}QsM^s<RRc6."at
00000010: 14d0 69ba 76f1 ac59 6422 346c            ..i.v..Yd"4l

Try it online.

Extremely Astronomically inefficient. The time and space complexity is O(16n) O(24n).

Explanation

First, a Q is implicitly appended.

s}QsM^s<RRc6."…"4lQ     Implicit: Q = input
            ."…"        Generate "logsincostanlnee"
          c6            Split in 6 parts: ["log", "sin", "cos", "tan", "ln", "ee"]
         R      4       For each n from 0 to 3
       <R               Take the first n chars from each string
      s                 Flatten the results
                 lQ     Get length of input
     ^                  Take that Cartesian power of the list
   sM                   Join each resulting list
 }Q                     Check if the input is found
s                       Cast to integer
\$\endgroup\$
1
  • \$\begingroup\$ The OP has clarified; you don't need the s. \$\endgroup\$
    – Dennis
    Dec 5, 2016 at 20:07
6
\$\begingroup\$

Jelly, 32 31 30 28 26 bytes

ŒṖw@€€“¡Ṡ[ẈKœịḲ-¢ȧ?-»’%3ḌẠ

Outputs 0 if the word is typeable, 1 if not. Thanks to @JonathanAllan for golfing off 1 byte!

Try it online!

How it works

ŒṖw@€€“¡Ṡ[ẈKœịḲ-¢ȧ?-»’%3ḌẠ  Main link. Argument: s (string of lowercase letters)

ŒṖ                          Yield all partitions of s.
      “¡Ṡ[ẈKœịḲ-¢ȧ?-»       Yield "sine  logcostanln". Every third character
                            marks the start of a typeable word.
  w@€€                      Find the indices of all substrings in the partitions
                            in the string to the right (1-based, 0 if not found).
                     ’      Decrement. "Good" indices are now multiples of 3.
                      %3    Modulo 3. "Good" indices are mapped to 0, "bad"
                            indices are mapped to 1 or 2.
                        Ḍ   Convert from decimal to integer. A partition will
                            yield 0 iff all indices are "good".
                         Ạ  All; yield 0 if one or more integers are falsy (0), 1
                            if all integers are truthy (non-zero).
\$\endgroup\$
0
6
\$\begingroup\$

Brachylog, 33 bytes

Fixed a bug thanks to @Synoli.

~c:1a
,"sincostanloglneeee"@6e@[?

Try it online!

Outputs true. if typeable or false. otherwise.

Explanation

We try deconcatenations of the input until we find one for which all strings that we concatenate are a prefix of one of ["sin", "cos", "tan", "log", "lne", "eee].

~c                          A list of strings which when concatenated results in the Input
  :1a                       All strings of this list satisfy the predicate below:
  
,"sincostanloglneeee"@5           The list ["sin", "cos", "tan", "log", "lne", "eee"]
                       e          Take one element of that list
                        @[?       The input is a prefix of that element
\$\endgroup\$
4
  • \$\begingroup\$ Why the . after true? \$\endgroup\$
    – user41805
    Dec 4, 2016 at 18:22
  • 1
    \$\begingroup\$ @KritixiLithos In SWI-Prolog when a query is true it prints true. and false. otherwise. i just reimplemented this: if the output is ignored (as it is here) and there is no writing to STDOUT, then it will print true. or false. depending on whether the main predicate succeeds or fails. It has a dot in SWI-Prolog because true. and false. are actually valid programs which always succeeds/always fails. \$\endgroup\$
    – Fatalize
    Dec 4, 2016 at 18:26
  • \$\begingroup\$ How does this solution work without tan explicitly appearing in the list? \$\endgroup\$
    – Synoli
    Dec 11, 2016 at 23:49
  • 2
    \$\begingroup\$ @Synoli It doesn't. I fixed it at the cost of 3 bytes, thanks. \$\endgroup\$
    – Fatalize
    Dec 12, 2016 at 7:53
4
\$\begingroup\$

Perl 6,  60 50  44 bytes

first attempt (60)

put +?(get~~/^<{<sin cos tan ln log e>».&{|m:ex/^.+/}}>*$/)

translation of the Perl 5 answer (50)

put +?(get~~/^[s[in?]?|co?|t[an?]?|ln?|log?|e]*$/)

using -n switch (43+1)

put +?/^[s[in?]?|co?|t[an?]?|ln?|log?|e]*$/

The first ? converts the result to Boolean, and the first + converts that to a number (1 for True, 0 for False)

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

Mathematica, 101 bytes

If[StringMatchQ[#,("sin"|"si"|"s"|"cos"|"co"|"c"|"tan"|"ta"|"t"|"ln"|"l"|"log"|"lo"|"e")..],"T","F"]&

It seems that the hard parts of this challenge are coming up with the shortest regex and choosing the most concise language to match the regex. I don't have anything to contribute to the former, and Mathematica isn't good candidate for the latter since you have to use StringMatchQ and RegularExpression. What I can do is answer your P.S.: is "tasteless" the longest word that you can type?

In[1]:= f=StringMatchQ[#,("sin"|"si"|"s"|"cos"|"co"|"c"|"tan"|"ta"|"t"|"ln"|"l"|"log"|"lo"|"e")..]&;

In[2]:= Select[EntityList@"Word",f@#[[2,1]]&][[All,2,1]]//SortBy[StringLength]//DeleteDuplicates
Out[2]= {c,e,l,s,t,cc,cl,el,et,lo,si,sl,ta,te,ccc,col,cos,cot,eel,ell,eta,etc,lee,let,log,lot,sec,see,set,sic,sin,sit,tae,tan,tat,tec,tee,cell,clog,clot,coco,cole,colt,coss,cost,cote,else,less,loco,loge,loll,lose,loss,lota,sect,seel,sell,sess,seta,sett,sill,silo,silt,sine,sise,siss,site,sloe,slog,slot,stet,taco,tact,tael,talc,tale,tall,tect,tell,test,cello,close,cosec,costa,cotan,eccle,elect,elsin,ettle,loess,lotte,secle,setee,since,sleet,stale,stall,state,steel,stele,tasse,taste,tatee,teest,telco,testa,tetel,tsine,cellco,closet,coleta,collet,coltan,cosine,cosset,costal,ecesis,estate,lessee,scelet,select,sellee,sestet,settee,settle,siesta,silole,stacte,stance,stasis,tallet,tallot,taslet,tassel,tasset,tattle,tectal,teetan,tellee,testee,tsetse,celesta,cessile,cocotte,collect,costate,ecolect,ectasis,electee,sessile,sinless,sitelet,statant,tassell,tastant,testate,coestate,colessee,colocolo,cosiness,costless,electant,lossless,sceletal,siletane,statelet,tactless,tallness,telltale,telocoel,coelostat,sissiness,stateless,tasteless,tattletale}

So "tattletale" seems to be the longest by one character.

\$\endgroup\$
6
  • \$\begingroup\$ Why is this non-competing? Does the function f you define not comply with the rules of the challenge? \$\endgroup\$
    – Dennis
    Dec 4, 2016 at 17:23
  • \$\begingroup\$ No, it outputs True or False rather than a single character. \$\endgroup\$
    – user61980
    Dec 4, 2016 at 17:25
  • \$\begingroup\$ Posts tagged with decision-problem are about determining True/False, so I think this is competing. \$\endgroup\$
    – FlipTack
    Dec 4, 2016 at 17:29
  • \$\begingroup\$ Non-competing status is reserved for submissions that use a language or language feature that didn't exist or had a bug when the challenge was posted. \$\endgroup\$
    – Dennis
    Dec 4, 2016 at 17:29
  • \$\begingroup\$ Fair enough. I edited my answer to have an actual solution. \$\endgroup\$
    – user61980
    Dec 4, 2016 at 17:36
2
\$\begingroup\$

Wonder, 41 bytes

!.(mstr"^(s|t|co?|(l|ta|si)n?|log?|e)+$")

Usage:

(!.(mstr"^(s|t|co?|(l|ta|si)n?|log?|e)+$")) "tasteless"

Totally misunderstood the question before, but now it's all fixed. Outputs F for match and T for no match.

Noncompeting, 35 bytes

!.`^(s|t|co?|(l|ta|si)n?|log?|e)+$`

Usage:

(!.`^(s|t|co?|(l|ta|si)n?|log?|e)+$`) "tasteless"

This makes use of applicable regexes, which was implemented after this challenge.

\$\endgroup\$
6
  • 2
    \$\begingroup\$ From my understanding, you can only delete the last character in the calculator, which means n in not typable but sin and ln are. \$\endgroup\$
    – Sunny Pun
    Dec 4, 2016 at 16:23
  • \$\begingroup\$ This regex detects sa as true \$\endgroup\$
    – user41805
    Dec 4, 2016 at 16:27
  • 1
    \$\begingroup\$ This fails the test case got. \$\endgroup\$
    – Dennis
    Dec 4, 2016 at 16:30
  • \$\begingroup\$ I believe it's fixed. \$\endgroup\$ Dec 4, 2016 at 16:57
  • \$\begingroup\$ The ? make the sub expression docile; see rexegg.com/regex-quantifiers.html#cheat_sheet. But even making it greedy with ?? or possessive with ?+ doesn´t work for some reason (at least in PHP). coelostats triggers the trap (at lo and ta). \$\endgroup\$
    – Titus
    Dec 6, 2016 at 12:19
2
\$\begingroup\$

Processing, 223 bytes

int d(String b){for(String p:"tan0log0sin0cos0ln0e".split("0"))if(p.startsWith(b))return 1;return 0;}

int l(String p){String b="";int t=1;for(char c:p.toCharArray()){b+=c;if(d(b)<1){b=c+"";t=d(b);}if(t<1)return t;}return t;}

Finally decided to do an answer without regex. To call the function, use l("tasteless");. Returns 0 for false and 1 for true.

Expanded code with explanation

int d(String b){
  for(String p:"tan0log0sin0cos0ln0e".split("0"))
    if(p.startsWith(b))
      return 1;
  return 0;
}
//main function
int l(String p){
  String b="";
  int t=1;
  for(char c:p.toCharArray()){
    b+=c;
    if(d(b)<1){
      b=c+"";
      t=d(b);
    }
    if(t<1)return t;
  }
  return t;
}

Basically, we iterate over the given string, building up b char by char. We check using d() if any of the tan,log,... start with b. If it does, then it is valid. Else, we check if the char at that position is valid and we reset b. Now if it is invalid, 0 will be returned, or else it will still be valid. At the end, if the program hasn't already returned something, then return 1.

\$\endgroup\$
2
  • \$\begingroup\$ Can't the d function return a bool? \$\endgroup\$ Dec 5, 2016 at 13:55
  • \$\begingroup\$ @RomanGräf It is more bytes to use boolean than int. Also true and false require more bytes than 1 and 0 \$\endgroup\$
    – user41805
    Dec 5, 2016 at 14:03
2
\$\begingroup\$

Scala, 49 bytes

s=>s.matches("(s(in?)?|co?|t(an?)?|ln?|log?|e)+")

Returns true if the given string matches the regex, false otherwise.

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

Python 3, 154 bytes

r=1;i=input()
while r:
 r=0
 for s in'sin,cos,tan,log,ln,co,lo,si,ta,s,c,t,l,e'.split(','):
  if i.startswith(s):r=i=i.replace(s,'',1);break
print(i=='')

Try it online!

\$\endgroup\$
2
  • 1
    \$\begingroup\$ You might need to check indentation, I had to edit the code to make it work \$\endgroup\$
    – george
    Dec 5, 2016 at 10:02
  • \$\begingroup\$ @george, weird, I tried in IDLE and the online compiler, and both seem to work \$\endgroup\$
    – Henke
    Dec 5, 2016 at 17:38
2
\$\begingroup\$

Python 3, 149 130 bytes

i=input()
for a in 'tan,sin,cos,log,si,co,ta,lo,lo,ln,s,c,t,l,e'.split(','):
    if a in i:
        i=i.replace(a,"")
print(not i)

edit #1: shaved 19 bytes using @Henke solution

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

Python 2, 124 bytes

f=lambda w:any(f(w[len(s):])if w[:len(s)]==s else 0for s in'sin,cos,tan,log,ln,co,lo,si,ta,s,c,t,l,e'.split(','))if w else 1
\$\endgroup\$
0
\$\begingroup\$

PHP, 60 bytes

<?=preg_match("#^((si|ta|l)n?|co?|log?|s|e|t)+$#",$argv[1]);

regex stolen from ETHproductions:
takes input from command line argument; prints 1 for typable, 0 for not typeable.

older versions, 75 73 69 bytes

<?=!preg_replace("#co|log|ln|lo|sin|si|tan|ta|[celst]#","",$argv[1]);

replaces all possible words with empty string, returns the result, negates.

<?=!preg_split("#co|log|ln|lo|sin|si|tan|ta|[celst]#",$argv[1],-1,1);

splits input by regex matches. Flag 1 stands for PREG_SPLIT_NO_EMPTY and tells preg_split to only return non-empty results. If input is typable, preg_split will only have empty results, so it will return an empty array, which is falsy. ! negates the result.

Both versions take input from command line argument
and print 1 if result is empty (input is typable), else nothing.

Notes:
Packing the regex using ? does not work here; it renders the expressions ungreedy; probably due to backtracking. And the order of the alternatives is important: ta has to stand before t or the engine will stop matching when it finds t.

I found a quantifier cheat sheet, thought ?? or ?+ might help; but they did not work for me.

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

Java 8, 55 bytes

s->return s.matches("^((si|ta|l)n?|co?|log?|[ste])+$");

Disclamer: I used ETHproductions' regex because it was many bytes shorter than mine. Full credit on the Regex to him. What I did was add 24 bytes to make it a Java function.

Returns false if the word did not fit into the Regex, else true.

\$\endgroup\$

Your Answer

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

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