嘶嘶声到文本


29

介绍

我不特别知道嘶嘶声的趋势来自何处。它可能只是一个模因之类的东西,但是却颇受欢迎。

挑战

今天的工作是将Fizz Buzz分别转换为二进制(0、1),然后将该二进制转换为文本。很标准的东西。

这是如何运作的?

FizzBu​​zzBuzzFizzBu​​zzFizzFizzFizz FizzBu​​zzBuzzFizzBu​​zzFizzFizzBu​​zz将转换为01101000 01101001,然后将其转换为“ hi”

约束条件

  • 输入是从二进制角度看的Fizz Buzz(请参见下面的示例。)
  • 输出必须是文本。
  • 您可以假设FizzBu​​zz输入正确。
  • 这是,最短字节获胜。

输入值

FizzBu​​zzBuzzFizzBu​​zzFizzFizzFizz FizzBu​​zzBuzzFizzBu​​zzFizzFizzBu​​zz FizzFizzBu​​zzFizzFizzFizzFizzBu​​zzBuzz

输出量

“嗨!”


15
模因?这是主要的(初级)学校游戏
Beta Decay's

2
我们不能在输入中使用空格吗?
HyperNeutrino

2
但是我们不能占用那个空间吗?如果不必输入该空间,我可以节省三个字节。
HyperNeutrino

10
FizzBu​​zz在Stack Exchange上享有很高的收益,部分原因是Joel(一位创始人)在其博客文章中引用了另一个博客,该博客谈到将其用作编程申请人的入门门槛低的问题
dmckee

8
@dmckee是Joel引用的“另一个博客”,它是Stackoverflow的另一位创始人Jeff Atwood。
pilsetnieks

Answers:


55

C,59个字节

i;f(char*s){while(*s&3?*s&9||(i+=i+*s%5):putchar(i),*s++);}

魔术数字,无处不在的魔术数字!

(而且,C比Python,JS,PHP和Ruby短吗?闻所未闻!)

该函数将字符串作为输入并输出到STDOUT。

演练

基本结构是:

i;           // initialize an integer i to 0
f(char*s){
while(...);  // run the stuff inside until it becomes 0
}

在这里,“里面的东西”是一堆代码,后跟,*s++,其中逗号运算符仅返回其第二个参数的值。因此,它将在退出之前遍历字符串并设置*s为每个字符,包括尾随的NUL字节(因为后缀++返回了先前的值)。

让我们看看其余的:

*s&3?*s&9||(i+=i+*s%5):putchar(i)

去除三元和短路||,可以扩展为

if (*s & 3) {
    if (!(*s & 9)) {
        i += i + *s % 5;
    }
} else {
    putchar(i);
}

这些魔术数字从何而来?这是涉及的所有字符的二进制表示形式:

F  70  01000110
B  66  01000010
i  105 01101001
z  122 01111010
u  117 01110101
   32  00100000
\0 0   00000000

首先,我们需要将空格和NUL与其余字符分开。该算法的工作方式是保留“当前”数字的累加器,并在到达空格或字符串末尾(即'\0')时将其打印出来。通过注意' ''\0'是唯一没有设置两个最低有效位中的任何一个的字符,0b11如果字符为空格或NUL ,我们可以对字符进行按位与运算以使其为零,否则为非零。

Digging deeper, in the first "if" branch, we now have a character that's one of FBizu. I chose only to update the accumulator on Fs and Bs, so I needed some way to filter out the izus. Conveniently, F and B both have only the second, third, or seventh least significant bits set, and all the other numbers have at least one other bit set. In fact, they all have either the first or fourth least significant bit. Hence, we can bitwise AND with 0b00001001, which is 9, which will yield 0 for F and B and nonzero otherwise.

一旦确定了For B,就可以通过将它们的模数5 映射到01,因为Fis 70Bis 66。然后是片段

i += i + *s % 5;

只是一种打招呼的方式

i = (i * 2) + (*s % 5);

也可以表示为

i = (i << 1) | (*s % 5);

这会将新位插入最低有效位置,并将其他所有位置移到1以上。

“可是等等!” 你可能会抗议。“打印后i,什么时候可以将其重置为0?” 好吧,putchar将其参数强制转换为unsigned char,恰好大小为8位。这意味着超过第8个最低有效位(即以前的迭代中的垃圾)的所有内容都将被丢弃,并且我们不必为此担心。

Thanks to @ETHproductions for suggesting to replace 57 with 9, saving a byte!


Nice trick with the putchar.
Computronium

This is reeeeeally awesome. C did right!
Gustavo Maciel

13
Speaking of doing things right, this is, in my not-so-humble opinion, how a code-golf answer should be done. You post a clever, insightful solution, accompanied by a complete, well-written explanation that actually teaches people something about the language that might be useful in other, more practical circumstances.
Cody Gray

3
@CodyGray Exactly this. One of the reasons that Code Golf isn't on the top of my SE that I visit frequently is because a lot of answers are just "here's the code". While that's cool for people who are very familiar with the languages, it just looks like noise to me. I like to see the explanations like here because it reveals the method, which I would think most people find a lot more interesting than the code itself. Just my two cents...
Chris Cirefice

Very nice bithack, but you count your bits from MSB(left) to LSB(right)? IMO the only sane way to count bits in an 8-bit byte (or a 128-bit SIMD vector, or whatever) is from LSB=bit 0 to MSB=bit 7.
Peter Cordes



9

Python 3, 169 101 93 91 85 81 bytes

lambda s,j="".join:j(chr(int(j('01'[b<"C"])for b in c[::4]),2))for c in s.split())

Try it online!

Explanation:

lambda s,j="".join:  # Create a lambda function
    j(  # call "".join, adds characters together with nothing in between
        chr(  # character by int
            int(  # string to int
                j(  # "".join again
                    '01'[b<"C"]  # 1 or 0, based on what character we get
                    for b in c[::4]  # For every first of 4 characters
                ),
                2)  # Base 2
        )
        for c in s.split()  # for every group of Fizz and Buzz with any whitespace character after it
    )

That was fast. +1
HyperNeutrino

I did something similar to this a while ago, it was just a matter of copy-paste and change it to FizzBuzz :P
Martmists

1
Oh that explains. :P But you got outgolfed ;_;
HyperNeutrino


1
Whoops, did it again, 85 bytes this time with a lambda function
Mr. Xcoder

8

JavaScript (ES6), 80 79 bytes

let f =

s=>`${s} `.replace(/.{4} ?/g,m=>m[s=s*2|m<'F',4]?String.fromCharCode(s&255):'')

console.log(f("FizzBuzzBuzzFizzBuzzFizzFizzFizz FizzBuzzBuzzFizzBuzzFizzFizzBuzz FizzFizzBuzzFizzFizzFizzFizzBuzz"))


Very nice. I tried and failed to come up with something shorter, though there are several alternate 80-byte solutions using .replace(/..zz/g,, '0b'+, etc.
ETHproductions

@ETHproductions Getting rid of n allows to reach 79. Sadly, this requires an extra space to be added to the input. Hence the rather costly `${s} ` .
Arnauld

7

Japt, 26 24 19 17 bytes

¸®ë4 ®c u5Ãn2 dÃq

Try it online!

Saved 2 bytes thanks to @Shaggy & 2 bytes thanks to @ETHproductions

Explanation

input: "FizzBuzzBuzzFizzBuzzFizzFizzFizz FizzBuzzBuzzFizzBuzzFizzFizzBuzz FizzFizzBuzzFizzFizzFizzFizzBuzz"

¸®                // ["FizzBuzzBuzzFizzBuzzFizzFizzFizz","FizzBuzzBuzzFizzBuzzFizzFizzBuzz","FizzFizzBuzzFizzFizzFizzFizzBuzz"]
  ë4              // ["FBBFBFFF","FBBFBFFB","FFBFFFFB"]
     ®c           // [[70,66,66,70,66,70,70,70],[70,66,66,70,66,70,70,66],[70,70,66,70,70,70,70,66]]
        u5Ã       // ["01101000","01101001","00100001"]
           n2     // [104,105,33]
              d   // ["h","i","!"]
               Ãq // "hi!"

1
You can replace the 2 }) with Ã. There's definitely more to be saved than that but I can't quite get it working on my phone.
Shaggy

1
Very nice, thanks for using Japt! You can save a couple bytes by replacing ò4...q n2 with ë4...n2 (ë4 does the same thing as ò4, except returning only the first item; strangely, it doesn't seem to be documented)
ETHproductions

1
@ETHproductions Thanks for making Japt!
powelles

6

Ruby, 65 63 60 bytes

->s{s.split.map{|x|x.gsub(/..../){$&.ord%5}.to_i(2).chr}*''}

This is an anonymous proc that takes input and gives output as a string.

->s{
s.split            # split on whitespace
.map{|x|           # for each word as x,
  x.gsub(/..../){  # replace each sequence of four characters with
    $&.ord%5       # the ASCII value of the first character, mod 5
                   # F is 70, B is 66, so this yields 0 for Fizz and 1 for Buzz
  }.to_i(2)        # interpret as a binary number
  .chr             # the character with this ASCII value
}*''               # join on empty string
}

6

JavaScript (ES6), 95 88 85 81 bytes

s=>s.replace(/..zz/g,m=>m<"F"|0).replace(/\d+ ?/g,m=>String.fromCharCode("0b"+m))

Try it

f=
s=>s.replace(/..zz/g,m=>m<"F"|0).replace(/\d+ ?/g,m=>String.fromCharCode("0b"+m))
oninput=_=>o.innerText=f(i.value)
o.innerText=f(i.value="FizzBuzzBuzzFizzBuzzFizzFizzFizz FizzBuzzBuzzFizzBuzzFizzFizzBuzz FizzFizzBuzzFizzFizzFizzFizzBuzz")
*{font-family:sans-serif}
<input id=i><p id=o>


I believe + is shorter than parseInt
Kritixi Lithos

2
I think +(m[0]<"F") could be shortened to m<"F"|0
ETHproductions

5

Perl 5, 33 Bytes

print(pack'B*',<>=~y/FB -z/01/dr)

Replaces 'F' and 'B' in the input with 0 and 1 respectively, and deletes the other characters. It then uses perl's pack function to turn this bit string into ASCII characters.


Wow this is golfed down to about half the size of my Perl 5 attempt. Kudos.
David Conrad

1
I believe you could make this considerably shorter using the -p0 command line option (which would save you <>=~r for input, and allow you to use $_= rather than print()). Depending on how you want to handle newlines, you might not even need the 0. (Even if you want to avoid command line option penalties, say is shorter than print.)

@Chris Not mine, faubiguy's. But thanks. ;)
David Conrad

@DavidConrad My bad haha.
Chris

1
You definitely don't need the 0 either. Just use the -p flag and $_=pack'B*',y/FB -z/01/dr for your program lowers your score to 26 bytes.
Chris

5

Python 2, 90 83 82 81 bytes

-1 byte thanks to totallyhuman
-1 byte thanks to Martmists
-1 byte thanks to Jonathan Frech

lambda x:''.join(chr(int(`[+(l<'D')for l in b[::4]]`[1::3],2))for b in x.split())

Try it online!



you can save a byte by turning *1 for into *1for
Martmists

Since you use *1 to convert from boolean to integer, you can save a byte by using a +: (l<'D')*1for can be +(l<'D')for.
Jonathan Frech

3

Whitespace, 123 bytes

Visible representation:

SSNNSSNSNSSSNSNSTNTSTTTSSSTSSSSSNTSSTSNSNTSSNSSSTSSTTSNTSSTNTSTNSSSTNTSSSNSSTNSSNSNSSNSTNTSTNTSTNTSTSSSNSNNNSSSNSNTTNSSNSNN

Unobfuscated program:

    push  0
loop:
    dup
    push  0
    dup
    ichr
    get
    push  32
    sub
    dup
    jz    space
    push  38
    sub
    jz    fizz
    push  1
    add
fizz:
    push  0
    dup
    dup
    ichr
    ichr
    ichr
    add
    jmp   loop
space:
    swap
    pchr
    jmp   loop

There's nothing particularly odd about the implementation, the only real golfing is in some strange reuse of temporaries as well as not caring about the unbounded stack growth to skim down some more bytes.


3

Octave, 59 57 53 bytes

@(s)['',bi2de(flip(reshape(s(65<s&s<71)<70,8,[]))')']

This doesn't work on TIO, since the communication toolbox is not implemented. It works fine if you copy-paste it to Octave-online. It's not even close to be working code in MATLAB.

Managed to save two bytes by transposing the matrix after flipping it, instead of the other way around.

Explanation:

@(s)             % Anonymous function that takes a string as input
    ['',<code>]  % Implicitly convert the result of <code> to its ASCII-characters

Let's start in the middle of <code>:

s(65<s&s<71)      % Takes the elements of the input string that are between 66 and 70 (B and F)
                  % This gives a string FBBFFBBFBBBFFFBF...
s(65<s&s<71)<70   % Converts the resulting string into true and false, where F becomes false.
                  % Transformation: FBBFFB -> [0, 1, 1, 0, 0, 1]

Let's call the resulting boolean (binary) vector for t.

reshape(t,8,[])       % Convert the list of 1 and 0 into a matrix with 8 rows, one for each bit
flip(reshape(t,8,[])) % Flip the matrix vertically, since bi2de reads the bits from the wrong end
flip(reshape(t,8,[]))' % Transpose it, so that we have 8 columns, and one row per character
bi2de(.....)'          % Convert the result decimal values and transpose it so that it's horizontal

3

Perl 5, 28 bytes+4 bytes for flags=32 bytes

Run with the flags -040pE

$_=chr oct"0b".y/FB -z/01/dr

-040 sets the record separator to a space so that perl sees each group of FizzBuzzes as a separate line, then loops over those lines, changing F to 0, B to 1, deleting everything else, then converting to binary and from there to ascii.




2

Brain-Flak, 107 bytes

{(((((()()()()){}){}){})({}[{}])()())((){[()](<{}>)}{}<>)<>{(<{}{}{}{}>)<>({}({}){})<>}{}}<>{({}<>)<>}<>

Try it online!

+3 bytes for the -c flag.

Explanation

{                                        For each character in input:
 (((((()()()()){}){}){})({}[{}])()())    Push 32-n and 66-n
 ((){[()](<{}>)}{}<>)<>                  If character is B, push 1 on second stack.  Otherwise, push 0
 {                                       If character is not space:
  (<{}{}{}{}>)                           Burn 3 additional characters
  <>({}({}){})<>                         Multiply current byte by 2 and add previously pushed bit
 }                                       (otherwise, the pushed 0 becomes the new current byte)
 {}                                      Remove character from input
}
<>{({}<>)<>}<>                           Reverse stack for output

2

q/kdb+, 41 40 37 33 bytes

Solution:

{10h$0b sv'66=vs[" ";x][;4*(!)8]}

Example:

q){10h$0b sv'66=vs[" ";x][;4*(!)8]}"FizzBuzzBuzzFizzBuzzFizzFizzFizz FizzBuzzBuzzFizzBuzzFizzFizzBuzz FizzFizzBuzzFizzFizzFizzFizzBuzz"
"hi!"

Explanation:

Split the input string on " " to give distinct lists of FizzBuzz..., index into each of these lists at the first character (ie 0 4 8 ... 28). Return boolean list determined by whether each character is "B" (ASCII 66). Convert these lists to base 10, and then cast result to string.

{10h$0b sv'66=vs[" ";x][;4*til 8]} / ungolfed solution
{                                } / lambda function with x as implicit input
              vs[" ";x]            / split (vs) input (x) on space (" ")
                           til 8   / til 8, the range 0..7 inclusive
                         4*        / vectorised multiplication, 0 1 2 3 => 0 4 8 12
                       [;       ]  / index the 2nd level at these indices (0, 4, 8 ... 28)
           66=                     / 66 is ASCII B, 66="FBBFBFFF" -> 01101000b
     0b sv'                        / join (sv) each row back with 0b (converts from binary)
 10h$                              / cast to ASCII (0x686921 -> "hi!")

1

Haskell, 72 bytes

(>>= \w->toEnum(foldl1((+).(2*))[mod(fromEnum c)5|c<-w,c<'a']):"").words

Try it online!

How it works

            words      -- split input string into words at spaces
(>>=      )            -- map the function to each word and flatten the resulting
                       -- list of strings into a single string
   \w->                -- for each word w
       [  |c<-w,c<'a'] -- take chars c that are less than 'a' (i.e. B and F)
     mod(fromEnum c)5  -- take ascii value of c modulus 5, i.e. convert to bit value
    foldl1((+).(2*))   -- convert list of bit to int
  toEnum(   ):""       -- convert ascii to char.  :"" forces toEnum to be of type String
                       -- now we have a list of single char strings, e.g. ["h","i","!"]        

1

JavaScript ES6 - 98 bytes

too many bytes, but at least readable

Defined as function it is 98 bytes

let s=>s.replace(/(F)|(B)|./g,(c,F,B)=>B?1:F?0:'').replace(/.{8}/g,v=>String.fromCharCode('0b'+v))

test:

"FizzBuzzBuzzFizzBuzzFizzFizzFizz FizzBuzzBuzzFizzBuzzFizzFizzBuzz FizzFizzBuzzFizzFizzFizzFizzBuzz"
.replace(/(F)|(B)|./g,(c,F,B)=>F?0:B?1:'').replace(/.{8}/g,v=>String.fromCharCode('0b'+v))

Explanation:

/(F)|(B)|./

Matches the F and B letters and anything else as Groups

(c,F,B)=>F?0:B?1:''

is a Function that captures the groups , returns a 0 for F and 1 for B , or ''

c is the character matched
F and B are now Parameters!
the 3rd . group is ommitted as parameter

F and B are undefined when the 3rd group is matched
B is undefined when group F is matched

The resulting 0100.. etc string

is cut in slices of 8 bytes

.replace(/.{8}/g,v=>String.fromCharCode('0b'+v))

and processed as 0b binary string


2
Welcome to PPCG! The objective of this challenge is to provide a program or function translating arbitrary FizzBuzz strings. I don' know much JavaScript, but a valid function submission might be s=>s.replace( .... Also please do include a byte count in the Header of your answer.
Laikoni

I cleaned up some of your code formatting for you. Also, you don't need the let, anonymous functions are acceptable.
Shaggy

1

shortC, 35 bytes

i;AW*@&3?*@&9||(i+=i+*s%5):Pi),*s++

Conversions in this program:

  • A - int main(int argc, char **argv){
  • W - while(
  • @ - argv
  • P - putchar(
  • Auto-inserted );}

Heavily based off of Doorknob's answer.


1

APL (Dyalog Classic), 17 bytes

{82DR'B'=⍵∩'BF'}

Explanation

           ⍵∩'BF'    cut, removes all but 'BF' from 
       'B'=          equals 'B' turns it into binary stream   
 82DR              converts into character stream

Try it online!



0

Google Sheets, 94 bytes

=ArrayFormula(JOIN("",CHAR(BIN2DEC(SPLIT(SUBSTITUTE(SUBSTITUTE(A1,"Fizz",0),"Buzz",1)," ")))))

I'm not familiar with FizzBuzz binary but it seems that they're delineated by spaces so this formula relies on that. The logic is pretty simple:

  • Replace Fizz with 0 and Buzz with 1
  • Split the result into an array using a space as a delimiter
  • Convert each element from binary to decimal
  • Replace each element with its ASCII equivalent
  • Join each element without a delimiter

0

Java 8, 117 115 bytes

s->{for(String x:s.split(" "))System.out.print((char)Long.parseLong(x.replace("Fizz","0").replace("Buzz","1"),2));}

I doubt you can do a lot of the fancy regex replacements in Java like most other answers, mainly because you can't do anything with the captured capture-groups in Java-regexes.. (I.e. "$1".charAt(...) or "$1".replace(...) aren't possible for example.)

Explanation:

Try it here.

s->{                          // Method with String parameter and no return-type
  for(String x:s.split(" "))  //  Loop over the input split by spaces:
    System.out.print(         //   Print:
     (char)                   //    Each character
     Long.parseLong(          //    after we've converted each binary-String to a long
      x.replace("Fizz","0").replace("Buzz","1")
                              //    after we've replaced the Fizz/Buzz to 0/1
     ,2));
}                             // End of method

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.