| Bytes | Lang | Time | Link |
|---|---|---|---|
| 193 | Nim | 250728T111931Z | janAkali |
| 236 | C GCC | 230224T171848Z | Peter |
| 227 | C# Visual C# Interactive Compiler | 200312T140920Z | Jirka Pi |
| 083 | Ruby | 200306T100047Z | G B |
| 176 | Python 3.8 | 200306T114359Z | Noodle9 |
| 027 | Japt | 200306T233950Z | Shaggy |
| 057 | Charcoal | 200306T224739Z | Neil |
| 021 | Jelly | 200306T195528Z | Nick Ken |
| 189 | R | 200306T181809Z | John |
| 023 | Husk | 200306T203934Z | Leo |
| 204 | Red | 200306T135134Z | Galen Iv |
| 093 | Perl 5 p MListUtil=product | 200306T184533Z | Xcali |
| 026 | Jelly | 200306T181953Z | Jonathan |
| 080 | JavaScript ES6 | 200306T095334Z | Arnauld |
| 206 | SNOBOL4 CSNOBOL4 | 200306T161811Z | Giuseppe |
| 155 | Python 3 | 200306T131425Z | ovs |
| 026 | 05AB1E | 200306T101317Z | Expired |
| 173 | Wolfram Language Mathematica | 200306T104849Z | ZaMoC |
| 020 | 05AB1E | 200306T094150Z | Kevin Cr |
| 070 | Retina | 200306T112642Z | Neil |
Nim, 193 bytes
include math,prelude
proc(a=""):auto=join a.split(' ').mapIt (var b,c=it[0..^2];[it,try:($b.parseInt.fac)except:(for i in 2..b.len:c&=b[0..^i];c),""][int(it[^1]<'"')+int it in ["1!","2!"]])," "
Exception behaviour:
"I have 2! games" -> "I have games"
Ungolfed:
proc facJoke(input = ""): auto =
join(
input.split(' ').mapIt (
var word, acc = it[0..^2] # substring (^2 == it.len-2)
[
it, # 1st element - unchanged word
(
try:
$word.parseInt.fac
except:
for i in 2..word.len:
acc &= word[0..^i]
acc
), # 2nd element - either fac(n) or transformed word if fails to parse integer
"" # 3rd element - empty string
][ int(it[^1] < '"') + int(it in ["1!","2!"]) ] # indexing array with sum of two bools
# [a-zA-Z] > '"' > '?'
# string in [array of strings]
# int(true) + int(true) == 2
),
" ") # second argument of join
C (GCC), 236 bytes
i,j;char*a,*b,*c,*d;f(char*s){a=strchr(s,33);for(b=a;b>s&!isspace(b[-1]);--b);for(d=s;d-b;)putchar(*d++);*a=0;if(*b&64)for(c=a;*b;*--c=0)printf("%s",b);else{for(i=j=1;j-atoi(b);)i*=++j;printf("%d",i);}printf("%s",a+1);j>0&j<3&&puts(b);}
Explanation:
i,j;
char*a,*b,*c,*d;
f(char*s)
{
// a is the first '!' in s
a=strchr(s,33);
// b is the first character after the first whitespace before a, or s if there is no whitespace there
for(b=a;b>s&!isspace(b[-1]);--b);
// Print each character from s to b
for(d=s;d-b;)putchar(*d++);
// Null-terminate s at a
*a=0;
// If b is alphabetic
if(*b&64)
// While there's anything left of b, print b and shorten it by adding an earlier null terminator
for(c=a;*b;*--c=0)
printf("%s",b);
else {
// If b is not alphabetic, it's assumed to be a number.
// i is the product of all j-s between 1, and b as a number
for(i=j=1;j-atoi(b);)i*=++j;
printf("%d",i);
}
// Print everything after the exclamation mark
printf("%s",a+1);
// Print the number twice if it's 1 or 2
j>0&j<3&&puts(b);
}
ceilingcat's 226 byte version:
i,j;char*a,*b,*c,*d;f(char*s){for(b=a=index(s,33);b>s&!isspace(b[-1]);--b);for(d=s;*a=d-b;)putchar(*d++);if(*b&64)for(c=a;*b;*--c=0)printf(b);else{for(i=j=1;j-atoi(b);)i*=++j;printf("%d",i);}printf("%s",a+1);j>0&j<3&&puts(b);}
C# (Visual C# Interactive Compiler), 227 bytes
s=>{var w=s.Split("!")[0].Split(" ").Last();if(!int.TryParse(w,out int n))for(;(w=w.Substring(1)).Length!=0;)s=s.Replace("!",w+"!");else{for(int i=n;--i>0;n=n*i);s=s.Replace(w+"!",""+(n<1?1:n>4?n:0));}return s.Replace("!","");}
Ruby, 88 85 83 bytes
->s{s.sub(/\S+!/){|z|z<?1?1:z>?9?z.chars.map{z=z.chop}*'':eval([6,*4..z.to_i]*?*)}}
Python 3.8, 222 \$\cdots\$ 178 176 bytes
Saved 2 bytes thanks to Kevin Cruijssen!!!
Added 2 3 bytes to fix bugs.
Saved 5 bytes thanks to ovs!!!
lambda j:(g:=re.match(r"(.*)\b(\w+)!(.*)",j).group)(1)+(str(math.perm(int(f)))*-~(f in'1 2')if(f:=g(2))[0]<':'else''.join(f[:i]for i in range(len(f),0,-1)))+g(3)
import math,re
Replaces 1! with 11 and 2! with 22.
Ungolfed:
import math,re
def f(j):
m=re.match(r"(.*)\b(\w+)!(.*)" ,j)
f=m.group(2)
if f in ('1','2'):f=''
if re.match(r"\d+",f):
f=str(math.factorial(int(f)))
else:
f=''.join(f[:i] for i in range(len(f),0,-1))
return m.group(1)+f+m.group(3)
Japt, 27 bytes
Well, this is just god-awful but I've spent far too long on it to not post it. Kids, let this be a warning against trying to golf without your full faculties about you!
r"(%w+)!"Ï<3?°Y:Yn ʪYå+ Ôq
Charcoal, 57 bytes
≔⌕θ!η≔⊟⌕A⁺ …θη ζ…θζ≔✂θζη¹ε≡ε2¹1¹0¦1¿ΣεIΠ…·¹Iε⭆ε…ε⁻Lεκ✂θ⊕η
Try it online! Link is to verbose version of code. Explanation:
≔⌕θ!η
Find the position of the !.
≔⊟⌕A⁺ …θη ζ
Find the position of the start of the word ending in !.
…θζ
Print the prefix of the input before the word.
≔✂θζη¹ε≡ε
Extract the word and switch on it.
2¹1¹0¦1
If it's 2 or 1 then output a -, otherwise if it's a 0 then outputs 1, otherwise...
¿ΣεIΠ…·¹Iε
... if it's an integer then output its factorial, otherwise...
⭆ε…ε⁻Lεκ
... output its prefixes in reverse order. (These are inclusive prefixes, so they include the word itself but not the empty string.)
✂θ⊕η
Print the suffix of the input after the !.
Jelly, 22 21 bytes
;”ḟV$¹ƤṖṚƊfØD$?ċ¡€”!K
A full program taking a list of words and printing a string. For the 1 and 2 cases the 1 and 2 are dropped from the output. Saved a byte now that a list of words is permitted input.
Explanation
Ḳ | Split at spaces
ċ¡€”! | For each word, if it contains a ! then do the following:
fØD$? | - If any characters are left after restricting to digits:
$ | - Then:
;”ḟ | - Append ḟ (which will filter out 1 and 2 at the next step because x! == x)
V | - Evaluate (i.e. calculate the factorial and then filter out the input number)
Ɗ | - Else:
¹Ƥ | - Prefixes
Ṗ | - Remove last
Ṛ | - Reverse
K | Finally, join with spaces
R, 205 201 194 189 bytes
s=scan(,'');l=grepl('\\w!',s);w=s[l];n=nchar;k=function(m,x)substr(x,1,n(x)-m);w=k(1,w);b=as.numeric(w);o=`if`(is.na(b),Reduce(paste0,sapply(0:n(w)-1,k,w)),gamma(b+1));s[l]=o;if(o!=w)cat(s)
Husk, 23 bytes
wm??öΣ↔ḣhȯs§Y→ΠiV√I€'!w
Maps 1 to 2 and 2 to 3.
On my phone, so writing an explanation is not too easy, but I'll try.
Code with parentheses for "clarity":
wm(?(?(Σ↔ḣh)(s§Y→Πi)(V√))(I)(€'!))w
The main part of the code is built by conditionals, they may be a bit easier to read once you know that in Husk ?abc means if c then a else b. Also, most of the time fg is the composition of functions f and g, so we are actually applying g before f (to oversimplify it, you should read the code backwards)
A pseudocode for this could be:
Split input into words, map the following function to each word and then join them again.
If word contains '!':
if any character of the word is a letter:
get the head of the word (drop final '!')
get the list of prefixes (heads)
reverse it
join prefixes in a single word
else:
convert word to integer
get the maximum between:
successor
factorial
convert it back to string
else apply Identity function (do nothing)
Red, 242 225 204 bytes
func[s][c: charset[#"0"-#"z"]parse s[any[change[copy t any c"!"](
case[find"12"t[0]t <"A"[t: to 1 t p: 1 while[t > 1][p: p * t
t: t - 1]p]t >":"[p: copy t until[take/last p append t p p =""]t]])| skip]]s]
Perl 5 -p -MList::Util=product, 93 bytes
s/(\d+)!/$1?$1>2?product 1..$1:'':1/e;s%(\w+)!%join'',map{substr$1,0,$_}reverse 1..length$1%e
Jelly, 26 bytes
ḲṖVN!Ƒ¡!ƲṖƤṚ$<”AẠ$?¹ċ?€”!K
A full program accepting a string which prints the result.
Kind of a tough one for Jelly.
How?
ḲṖVN!Ƒ¡!ƲṖƤṚ$<”AẠ$?¹ċ?€”!K - Link: list of characters, s
Ḳ - split at space characters -> words
”! - literal '!' character
€ - for each word:
? - if...
ċ - ...condition: count (i.e. contains?)
? - ...then: if...
$ - ...condition: last two links as a monad:
< - less than:
”A - literal 'A' character
Ạ - all?
Ʋ - ...then: last four links as a monad:
Ṗ - pop (remove the '!')
V - evaluate as Jelly code (get an integer)
¡ - repeat...
Ƒ - ...number of times: is invariant under?:
! - factorial
N - ...action: negate (i.e. 1;2;X -> -1;-2;X)
! - factorial (-1! = -2! = inf)
$ - ...else: last two links as a monad:
Ƥ - for prefixes:
Ṗ - pop (remove the `!` from each prefix)
Ṛ - reverse
¹ - ...else: no-op
K - join with spaces
- implicit, smashing print
JavaScript (ES6), 86 85 80 bytes
Replaces 1! and 2! with 0 ... ¯\_(ツ)_/¯
s=>s.replace(/\w+!/,g=s=>(s=s.slice(0,-1))?1/s?'1006'[s]||s*g(s-1+'#'):s+g(s):s)
How?
Because the callback function of replace() is recursive, it's better to feed it with a single variable \$s\$ to keep the recursive calls short. That's why the ! is captured along with the word preceding it.
The same slice(0,-1) is used to remove the ! on the first iteration and to build the prefixes in case of a string. In order to make the factorial computation compatible with that method, we just have to pad the recursive argument with a random character which is immediately removed by the next iteration.
To deal with the weird edge cases \$1!\$ and \$2!\$, we use the small lookup string '1006' to stop the recursion whenever the value is less than or equal to \$3\$. This way, we have \$0!=1\$, \$3!=6\$ and \$n! = 6\cdot\prod_{k=4}^{n}k\$ for \$n>3\$, but \$1!=2!=0\$ (sic).
Commented
NB: alternate slash symbols used in the regular expression to prevent the syntax highlighting from being broken
s => // s = input string
s.replace( // find in s
⁄\w+!⁄, // a word followed by a '!'
g = s => // g is a recursive function computing the replacement
(s = s.slice(0, -1)) // remove the last character from s
// (for the 1st iteration, it removes the '!')
? // if the resulting string is not empty:
1 / s ? // if this is a numeric value:
'1006'[s] // 0 -> 1, 1 -> 0, 2 -> 0, 3 -> 6
|| s * // for all other values, multiply s by
g(s - 1 + '#') // the result of a recursive call with s - 1,
// padded with a '#' for the next slice()
: // else (s is a string):
s + // append s
g(s) // append the result of a recursive call
: // else (s is empty):
s // stop recursion
) // end of replace()
SNOBOL4 (CSNOBOL4), 206 bytes
D =84 ** 9
INPUT ARB . L SPAN(&UCASE &LCASE D) . W '!' REM . R
W SPAN(D) :F(S)
N =W + 1 LT(W,3) :S(O)
N =W
F W =W - 1 GT(W,1) :F(O)
N =N * W :(F)
S N =N W
W ARB . W RPOS(1) :S(S)
O OUTPUT =L N R
END
84 ** 9 = 208215748530929664 which has all the digits from 0-9.
D =84 ** 9 ;* D contains every decimal digit.
INPUT ARB . L SPAN(&UCASE &LCASE D) . W '!' REM . R
;* split the input into an ARBitrary Left part, the Word followed by a '!' and the REMainder to the Right part.
W SPAN(D) :F(S) ;* if W has digits, goto S
N =W + 1 LT(W,3) :S(O) ;* if W < 3, then N = W + 1 and goto O
N =W ;* set N = W
F W =W - 1 GT(W,1) :F(O) ;* decrement W, and goto O if W == 0
N =N * W :(F) ;* N = N * W, goto F
S N =N W ;* W is a string, so set N = N W
W ARB . W RPOS(1) :S(S) ;* set W to W excluding its final character and if W had any characters, goto S
O OUTPUT =L N R ;* output Left, the New middle, and the Right string.
END
Python 3, 157 155 bytes
Thanks to Noodle9 for -2 bytes.
(*s,w),b=map(str.split,input().split('!'))
w*=1+(w in'1 2')
if'A'>w:
i=int(w);w=1
while i:w*=i;i-=1
else:
d=w[:-1]
while d:w+=d;d=d[:-1]
print(*s,w,*b)
Wolfram Language (Mathematica), 173 bytes
StringRiffle[""<>If[Last@#=="!",If[NumberQ[n=ToExpression[""<>#]],If[n>0&&n<3&&First@#!="0","",ToString@n],Rest@NestList[Most,#,Length@#-1]],#]&/@Characters/@StringSplit@#]&
-2 bytes from @Kevin Cruijssen
05AB1E, 23 22 26 25 20 bytes
ð¡εD'!åi¨ÐaiηRJë2Lså+!]ðý
+4 bytes for two bug-fixes (+1 in case the input is without spaces; +3 for 0!)
-5 bytes thanks to @Grimmy.
Outputs 2/3 for 1!/2! respectively.
Try it online or verify all test cases.
Explanation:
ð¡ # Split the (implicit) input-string on spaces
# (NOTE: `#` cannot be used here if the input doesn't contain spaces)
ε # Map each part to:
D # Duplicate it
'!åi '# If it contains a "!":
¨ # Remove the last character (the "!")
D # Duplicate it
! # Take the factorial of it (strings remain the same)
s # Swap to get the duplicate value again
> # Increase it by 1 (strings remain the same)
η # Get the prefixes of this string/integer
R # Reverse it
J # And join them together to a single string
M # And then push the largest value on the stack
# (which is either the factorial-integer;
# or the integer+1 if it was 0, 1, or 2;
# otherwise it's the longest string, which is the joined prefixes)
] # Close both the map and if-statement
ðý # And join everything with space delimiter again
# (after which the result is output implicitly)
Retina, 70 bytes
' %/!$/&/^[012]!$/(`..
$.(*__
/\D./(^`.
$`
.+
*
_
$.>`$*
~`^
^.$*¶$$.(
Try it online! Link includes test cases. Explanation:
' %
Split the input into words.
/!$/&
Only process words that end in !.
/^[012]!$/(`
If this is 0!, 1! or 2!...
..
$.(*__
... then return the incremented digit. (Approach shamelessly stolen from @Arnauld.)
/\D./(
If the word contains a non-digit (excluding the trailing !)...
^`.
$`
... then replace it with all of its prefixes in reverse order. (Note that these are exclusive prefixes so that the empty prefix is included but the string including the ! is excluded.)
.+
*
_
$.>`$*
~`^
^.$*$n$$.(
Otherwise compute the factorial.