30
\$\begingroup\$

Task

Read in a possibly infinite text stream or file, outputting its contents until the word hello has been output, abiding to the following rules.

  • Once hello has been output, your code should quit immediately. It should not wait for a newline for example.

  • Your code should output as it goes. That is it shouldn't read in a huge amount of input and then start outputting.

  • If the stream/file doesn't contain hello, your code should just carry on outputting the input forever or until the end of the stream/file is reached.

  • This is a case sensitive challenge, so hello is not equal to Hello.

  • You may assume that the input consists solely of printable ASCII characters and newlines.

  • Your code can't expect that the text will be terminated by a newline or that there will be any newlines at all in the input. Also, you code cannot assume that it will run on a machine with an infinite amount of memory.

  • You may assume that your code will be called from an empty directory.

Example input stream

I once had a horse called hellopina.

Output

I once had a horse called hello

Tip

Run yes | tr -d \\n | <your program> to check if it works with infinite streams. If it doesn't print anything and/or leaks memory, the program doesn't comply with the spec. It should print yyyyyyyyyyyyyyyyyyyyyy... forever with no newlines.

\$\endgroup\$
3
  • 1
    \$\begingroup\$ Are we permitted to read anything after the "hello"? The question seems to prohibit any additional read, which could be problematic in languages such as (Standard) C, that provide buffered input with automatic read-ahead. \$\endgroup\$ Apr 6, 2017 at 11:00
  • \$\begingroup\$ You should probably change teh accepted answer to the assembly one, as it's 2 byte shorter. \$\endgroup\$
    – Riker
    Apr 18, 2017 at 14:49
  • \$\begingroup\$ @Riker It would be great if someone could test it or at least say they believe it works first. \$\endgroup\$
    – user9206
    Apr 18, 2017 at 14:50

36 Answers 36

26
\$\begingroup\$

C (gcc), 81 80 76 75 72 71 70 69 bytes

main(n,c){while(~(c=getchar())&n-0xb33def<<7)n=n<<5^putchar(c)/96*c;}

Try it online!

How it works

This is a full program. We define a function f for our purposes. To save bytes, it is declared with two argument that default to int. This is undefined behavior, but in practice, n will be initialized as 1 when running the program without additional arguments, c will hold the lower 32 bits of the pointer to the argument vector

While the condition

~(c=getchar())&n-0xb33def<<7

holds, we'll execute the while loop's body:

n=n<<5^putchar(c)/96*c

To fully understand the condition, we must first examine the body. For now, all we observe is that c=getchar() reads a single byte from STDIN (if possible) and stores it in the variable c.

The byte sequence hello looks as follows in different representations.

char     decimal     binary (8 bits)
'h'      104         0 1 1 0 1 0 0 0
'e'      101         0 1 1 0 0 1 0 1
'l'      108         0 1 1 0 1 1 0 0
'l'      108         0 1 1 0 1 1 0 0
'o'      111         0 1 1 0 1 1 1 1

All of these fall in the range [96, 192), so c/96 will evaluate to 1 for each of these bytes, and to 0 for all remaining ASCII characters. This way, putchar(c)/96*c (putchar prints and returns its argument) will evaluate to c if c is `, a lowercase letter, one of {|}~, or the DEL character; for all other ASCII characters, it will evaluate to 0.

n is updated by shifting it five bits to the left, then XORing the result with the result from the previous paragraph. Since an int is 32 bits wide (or so we assume in this answer), some of the shifted bits might "fall off the left" (signed integer overflow is undefined behavior, but gcc behaves as the x64 instruction it generates here). Starting with an unknown value of n, after updating it for all characters of hello, we get the following result.

 n  ?????????????????????????|???????
'h'                          |    01101000
'e'                          |         01100101
'l'                          |              01101100
'l'                          |                   01101100
'o'                          |                        01101111
-----------------------------+--------------------------------
    <------ discarded ------>|???????0101100110011110111101111

Note that the lower 25 bits form the integer 0xb33def, which is the magic constant in the condition. While there is some overlap between the bits of two adjacent bytes, mapping bytes below 96 to 0 makes sure that there aren't any false positives.

The condition consists of two parts:

  • ~(getchar()) takes the bitwise NOT of the result of reading (or attempting to read) a byte from STDIN.

    If getchar succeeds, it will return the value of the read byte as an int. Since the input consists entirely of ASCII characters, the read byte can only have its lower 7 bits set, so the bitwise NOT will have its highest 25 bits set in this case.

    If getchar fails (no more input), it will return -1 and the bitwise NOT will be 0.

  • n-0xb33def<<7 subtracts the magic constant from before from n, then shifts the result 7 units to the left.

    If the last 5 read bytes were hello, the lowest 25 bits of n will be equal to 0xb33def and the subtraction will zero them out. Shifting the difference will yield 0 as the 7 highest bits will "fall off the left".

    On the other hand, if the last 5 read bytes were not hello, one of the lowest 25 bits of the difference will be set; after shifting, one of the highest 25 bits will be.

Finally, if getchar was successful and we didn't print hello yet, the bitwise AND, all of the highest 25 bits of the left operand and at least one of the highest 25 bits of the right one will be set. This way, & will yield a non-zero integer and the loop continues.

On the other hand, if the input is exhausted or we have printed hello already, one of the bitwise AND's operand will be zero, and so will the result. In this case, we break out of the loop and the program terminates.

\$\endgroup\$
5
  • \$\begingroup\$ You probably ought to mention that this depends on the input being encoded in ASCII, before delving into the explanation. \$\endgroup\$ Apr 6, 2017 at 11:08
  • 2
    \$\begingroup\$ @TobySpeight I don't think it's common to specify this. What kind of ASCII incompatible encoding would you expect a C answer to use? \$\endgroup\$
    – Dennis
    Apr 6, 2017 at 13:45
  • \$\begingroup\$ EBCDIC is the obvious encoding that's not ASCII. C doesn't prescribe any particular character encoding (only that the decimal digits must be represented by consecutive values, in order). \$\endgroup\$ Apr 6, 2017 at 14:45
  • \$\begingroup\$ the above program stop if the stream contain the not ascii string "«úá÷o" 1: o 111 6f 2: ÷ 246 f6 3: á 160 a0 4: ú 163 5: « 174 \$\endgroup\$
    – user58988
    Apr 10, 2017 at 15:14
  • \$\begingroup\$ @RosLuP The challenge spec guarantees that the input will consist of printable ASCII characters and newlines. \$\endgroup\$
    – Dennis
    Apr 10, 2017 at 18:32
19
\$\begingroup\$

Bash, 74 75 103 99 88 82 76 bytes

-10 bytes thanks to @DigitalTrauma!
-11 bytes thanks to @manatwork!
-6 bytes thanks to @Dennis!

IFS=
b=ppcg
while [ ${b/hello} ];do
read -rN1 a
b=${b: -4}$a
echo -n $a
done

Explanation:

IFS=    # making sure we can read whitespace properly
b=ppcg  # set the variable b to some arbitrary 4 letter string

while [ ${b/hello} ]; do  # while the variable b doesn't contain "hello", do the following
    read -rN1 a           # get input
    b=${b: -4}$a          # set b to its last 4 chars + the inputted char
    echo -n $a            # output the inputted char
done

Try it online!

\$\endgroup\$
1
  • 2
    \$\begingroup\$ This is great! I was hoping there would be a bash answer. \$\endgroup\$
    – user9206
    Apr 4, 2017 at 12:32
13
\$\begingroup\$

Labyrinth, 43 41 bytes

Thanks to Sp3000 for saving 2 bytes.

<_%-742302873844_::%*:*:420#+.:%):,*652_>

Try it online!

Explanation

The basic idea is to encode the last five characters in base 256 in a single integer. When a new character comes in, we can "append" it by multiplying the integer by 256 and adding the new code point. If we want to look only at the last 5 characters, we take the value modulo 2565 = 240 = 1099511627776. Then we can simply check whether this value is equal to 448378203247, which is what we get when we treat the code points of hello as base-256 digits.

As for the code... <...> is a bit of a Labyrinth idiom. It allows you to write an infinite loop without any conditional control flow on a single line, saving a lot of bytes on spaces and linefeeds. The main condition for this to work is that there are two disposable values on top of the stack when we reach the < (we normally use 0s for that, but the actual value is arbitrary).

Of course, the program does need some conditional logic to figure out when to terminate. But conditionally ending the program is possible by dividing by a value which is zero when we want the program to end. The <...> construct works by shifting the entire row left (cyclically) when the IP is at the left end, and then immediately shifting it back into position. This means that the code is actually executed right to left. Let's reverse it:

_256*,:)%:.+#024:*:*%::_448378203247-%_

This is one iteration of the loop which reads a character, terminates if we've reached EOF, prints the character, adds it to our encoding, truncates that to 5 characters, checks for equality with hello and repeats. Here is how that works in detail (remember that Labyrinth is stack-based):

_256*            Multiply the encoding by 256 in preparation for the next iteration.
,                Read one byte from STDIN.
:)%              Duplicate, increment, modulo. If we hit EOF, then , returns
                 -1, so incrementing and modulo terminates the program due to
                 the attempted division by zero. However, if we did read a
                 character, we've just compute n % (n+1), which is always n itself.
:.               Print a copy of the character we just read.
+                Add it to our encoding (we'll make sure to multiply the
                 encoding by 256 at the end of the iteration, so there's room
                 for our new character).
#024             Push 1024, using the stack depth to push the initial 1.
:*:*             Square it twice. That gives 2^40.
%                Take the encoding modulo 2^40 to truncate it to the last 5
                 characters.
::               Make two copies of the encoding.
_448378203247    Push the value that corresponds to "hello".
-                Subtract it from the encoding, giving zero iff the last 5
                 characters were "hello".
%                Take the other copy of the encoding modulo this value, again
                 terminating if we've reached "hello".
                 The actual value of this modulo - if it didn't terminate the
                 the program - is junk, but we don't really care, we just need
                 any disposable value here for the <...>
_                We push a zero as the second disposable value.
\$\endgroup\$
8
\$\begingroup\$

Brainfuck, 658 bytes

+[>,.>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++[-<->]+<[>-<[-]]>[-<,.>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++[-<->]+<[>-<[-]]>[-<,.>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++[-<->]+<[>-<[-]]>[-<,.>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++[-<->]+<[>-<[-]]>[-<,.>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++[-<->]+<[>-<[-]]>[-<<->>]]]]]<<]

Over 500 bytes are in the constants which I need to golf a bit.

It essentially is a state machine, so infinite input is not a problem.

This is the slightly commented version

+
[
  >,.
  >h
  [-<->]
  +<
  [
    >-<[-][in input spot, not h]
  ]
  >
  [
    -
    <
    [in input spot, h has been read]
    ,.
    >e
    [-<->]
    +<
    [
      >-<[-][in input spot, not e]
    ]
    >
    [
      -
      <
      [in input spot, e has been read]
      ,.
      >l
      [-<->]
      +<
      [
        >-<[-][in input spot, not l]
      ]
      >
      [
        -
        <
        [in input spot, l has been read]
        ,.
        >l
        [-<->]
        +<
        [
          >-<[-][in input spot, not l]
        ]
        >
        [
          -
          <
          [in input spot, l has been read]
          ,.
          >o
          [-<->]
          +<
          [
            >-<[-][in input spot, not o]
          ]
          >
          [
            -
            <
            [in input spot, o has been read]
            <->>
          ]
        ]
      ]
    ]
  ]
  <<
]
\$\endgroup\$
3
  • \$\begingroup\$ This looks fun :) \$\endgroup\$
    – user9206
    Apr 4, 2017 at 21:36
  • \$\begingroup\$ Welcome to Programming Puzzles and Code Golf StackExchange! \$\endgroup\$
    – betseg
    Apr 6, 2017 at 11:24
  • 2
    \$\begingroup\$ This code has multiple issues, but the biggest issue is that it doesn't include logic to handle cases like ahehellob properly; in the middle of a potential match, it only checks for the next letter in hello and doesn't look for an h to start over. \$\endgroup\$ Apr 6, 2017 at 17:06
8
\$\begingroup\$

Bash, 73 68 66 bytes

IFS=
[[ $1 != olleh ]]&&read -rN1 c&&echo -n $c&&exec $0 $c${1::4}

Assumes a directory with no or only hidden files. Must be run as <path/to/script>.

Try it online!

How it works (outdated)

At the beginning of the while loop, we first test if the string in the variable s (initially empty) equals olleh (hello backwards, olé), and return 0 (match) or 1 (not a match) accordingly. While formally part of the loop's condition, the outcome won't affect it on its own, as only the last command before do determines if the condition holds.

Next, we set the internal field separator to the empty string (so read won't choke on whitespace), read raw bytes (-r) from STDIN and store them in c. $? is the exit code of the previous command, so this reads exactly one (-N1) byte for a non-match and zero bytes (-N0). Reading zero bytes, whether its due to hitting EOF or because -N0 was specified, causes read to exit with status code 1, so the while loop will end; otherwise, the body is executed and we start over.

In the body, we first print the byte we read, then update s with s=$c${s::4}. This prepends the read byte to (up to) the first four bytes in s, so s will equal olleh once hello has been printed.

\$\endgroup\$
1
  • \$\begingroup\$ Very nice indeed! \$\endgroup\$
    – user9206
    Apr 4, 2017 at 19:48
8
\$\begingroup\$

brainfuck, 117 bytes

--->>>------>>>+>>>+>>>++++<,[.-----<-[>--<-----]<[<<<]>>>[<[<<<+>>>>->+<<-]>[>>
+>]<[+[-<<<]]>>[<+>-]>>]<[[-]<<<,<]>]

Formatted:

--->>>------>>>+>>>+>>>++++
<,
[
  .-----<-[>--<-----]<[<<<]
  >>>
  [
    <[<<<+>>> >->+<<-]
    >[>>+>]
    <[+[-<<<]]
    >>[<+>-]
    >>
  ]
  <[[-]<<<,<]
  >
]

Try it online.

This initializes the tape with the characters in hello offset by 107, spaced out with one value every three cells, then keeps track of the last five characters seen and checks for a match with every new character processed, using a flag to the right of the string to keep track of whether there has been a match.

\$\endgroup\$
7
\$\begingroup\$

Ruby, 46 60 bytes

a="";loop{q=$<.getc;~p if a[-5..-1]=="hello"||!q;a+=q;$><<q}

Try it online!

Reads characters from stdin until the last 5 are hello, then outputs the string (or until no characters are left in stdin). Terminates with error.

Equivalent to:

a = ""
loop {
    q = $<.getc
    ~p if a[-5..-1] == "hello" || !q
    a += q
    $><< q
}

Or, more ungolfed:

a = ""
loop do
    q = STDIN.getc
    break if a[-5..-1] == "hello" or not q
    a += q
    print q
end
\$\endgroup\$
2
  • 1
    \$\begingroup\$ a grows everytime a char is read. Does this crash if the input is infinite? \$\endgroup\$
    – betseg
    Apr 7, 2017 at 5:39
  • \$\begingroup\$ @betseg hm, maybe. Let me see if I can fix that \$\endgroup\$ Apr 7, 2017 at 5:41
7
\$\begingroup\$

Python 3, 120 116 104 Bytes

Works with infinite streams, first time golfing, any tips are appreciated.

import sys
a=1
c=''
while(a):
    a=sys.stdin.read(1)
    if a:print(end=a)
    c=(c+a)[-5:]
    if c=='hello':break

Thanks @DJMcMayhem for saving some bytes :)

\$\endgroup\$
2
  • \$\begingroup\$ Welcome to the site! c=[0,c+1]['hello'[c]==a] should save you some bytes. Also, a=1 is shorter too. \$\endgroup\$
    – DJMcMayhem
    Apr 4, 2017 at 20:11
  • 2
    \$\begingroup\$ You don't need the parenthesis for while in Python. \$\endgroup\$ Apr 5, 2017 at 13:40
7
\$\begingroup\$

Cubix, 94 83 82 79 63 56 bytes

p>q'-?w.uh'e@U7.'hqi?oqB-!ul.-..$WWu_q<o'\;>....6t?.../!@

Expanded:

        p > q '
        - ? w .
        u h ' e
        @ U 7 .
' h q i ? o q B - ! u l . - . .
$ W W u _ q < o ' \ ; > . . . .
6 t ? . . . / ! @ . . . . . . .
. . . . . . . . . . . . . . . .
        . . . .
        . . . .
        . . . .
        . . . .

Notes

  • The interpreter disables the input field when the program starts. As such, an infinite stream of input is impossible. This program takes the input character-by-character, so if it weren't for this limitation, it would work properly.
  • This program doesn't clear up the stack, and it gets messy very quickly. Since the machine this will be used on apparently can give infinite input streams, it seems reasonable to assume that it also has infinite memory.
  • Any and all golfing help is much appreciated.

Try it online

You can try the program here.

Explanation

General idea

The general idea is that we want to read a character, and then check it against varying characters (first h, then e, then l etc.). To keep track of the character we have missed, we keep it at the very bottom of the stack. When we need it, we can easily bring it to the top again.

Read/Write loop

The read-write loop is simply the 5th line. All characters that are not used are replaced by no-ops (.):

        . . . .
        . . . .
        . . . .
        @ . . .
' h q i ? o q B - ! u l . - . .
. . . . _ . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
        . . . .
        . . . .
        . . . .
        . . . .

This can be split up in two parts: Reading and (writing and checking). The first part contains the instructions up to and including the question mark. The second part up is the rest of the line. Because this loops around, we assume we start with a stack of [...]

    @
'hqi?
    _

Explanation
'h          Push the character code of the h
            Stack: [..., 104]
  q         Send it to the bottom
            Stack: [104, ...]
   i        Read one character of the input (-1 for EOF)
            Stack: [104, ..., input]
    ?       Start of condition:
              if (input < 0):
    @           execute '@', ending the program
              if (input = 0):
                continue going right
              if (input > 0):
    _           turn to the right, reflect back ('_') and
                turn right again, effectively not changing 
                the direction at all

The second part (writing and checking) is linear again. The stack starts as [next-char, ..., input]. We abstracted the next character, because that changes later in the program.

oqB-!ul.-  Explanation
o          Output the character at the top of the stack
 q         Send the input to the bottom of the stack
           Stack: [input, next-char, ...]
  B        Reverse the stack
           Stack: [..., next-char, input]
   -       Push the difference of the top two characters, which
           is 0 if both are equal, something else otherwise
           Stack: [..., next-char, input, diff]
    !      if (diff = 0):
     u       make a u-turn to the right
           else:
      l.     execute two no-ops
        -    push [input - next-char - input], which is disregarded
             later, so it effectively is a no-op as well.

Now, the IP will start again at the beginning of this loop, resetting the next character to check to h.

Matching the next character

If the IP made a u-turn (i.e. the character we read and printed matched the next character in 'hello'), we need to check what character the input was and depending on that, push the next character to the bottom of the stack. After that, we need to return to the read/write loop, without pushing h to the stack, so we need another way to get there.

First things first: determine what character the input was. The stack looks like this: [..., prev-char, input, 0].

        . . . .
        - ? . .
        u h ' e
        . . . .
. . . . . . . . . ! u . . . . .
. . . . . . . . . \ ; . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
        . . . .
        . . . .
        . . . .
        . . . .

To compare the input, we use the character code of h again. Initially, this was because I didn't really know how I was going to handle this and h is the first character in the string to check for, but it ended up being quite convenient. If we subtract the character code of h from the input, we get -3 if the input is e, 0 if the input is h, 4 if the input is l and 7 if the input is o.

This is useful, because the ? command lets us easily separate negative values from positive values and zero. As such, if the IP turns left, the difference was negative, so the input was e, so the next character should be an l. If the IP continues going straight, the difference was 0, so the input was h, so the next character should be an e. If the input is an l or an o, the IP turns right.

All instructions executed before the aforementioned question mark are:

;!e'h-     Explanation
;          Delete the top of the stack
           Stack: [..., prev-char, input]
 !         if (input = 0):
  e          execute 'e' (no-op)
   'h      Push the character code of h
           Stack: [..., prev-char, input, 104]
     -     Push the difference of the input and 104
           Stack: [..., prev-char, input, 104, diff]

Now the IP changes its direction as detailed above. Let's go over the different possibilities.

Input 'e'

First we'll consider the input e, which causes the IP to move upwards from the ?, since the difference is 3. All irrelevant characters have been removed from the cube.

        . > q '
        . ? . .
        . . . .
        . . . .
. . q . . . . . . . . l . . . .
$ W W . . . . . . . . > . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
        . . . .
        . . . .
        . . . .
        . . . .

The characters are executed in this order (excluding some control-flow characters):

q'l$WWq
q           Save the difference (-3) to the bottom of the stack so
            we can tell whether the l on the bottom of the stack is
            the first or the second l in hello
            Stack: [-3, ...]
 'l         Push the character code of l to the stack
            Stack: [-3, ..., 108]
   $W       no-op
     W      Sidestep into the loop
      q     Send the character code to the bottom
            Stack: [108, -3, ...]

Now the IP has reached the read/write loop again.

Input 'h'

If the input was 'h', the difference is 0, so the IP doesn't change its direction. Here's the cube again, with all irrelevant characters removed. Since this path includes quite a few no-ops, all the no-ops it passes have been replaced by &. The IP starts at the question mark.

        . . . .
        . ? w .
        . . ' e
        . . . .
. . . . . . . . . ! . . . . . .
. . . u _ q < . . \ . . . . . .
. . ? & & & / . . & . . . . . .
. . & . . . . . . & . . . . . .
        . . . .
        & & & &
        . . . .
        . . . .

The instructions executed are:

'e!\?q_
'e          Push the character code of the e
            Stack: [..., 101]
  !         if (101 = 0):
   \          reflect away (effectively a no-op)
    ?       if (101 > 0):
              turn right (always happens)
     q      Move 101 to the bottom of the stack
            Stack: [101, ...]
      _     No-op

And now we're entering the read/write loop again, so we're done.

Other inputs

All other inputs result in a positive difference, so the IP turns right at the question mark. We still need to separate the l and the o, so that's what we'll do next.

Separating the 'l' and 'o'

Keep in mind that the difference is 7 for o and 4 for l and that we have to end the program if the input was an o. Here's the cube again with the irrelevant parts replaced by a . and the no-ops the IP crosses have been replaced by ampersands.

        . . q .
        . ? w .
        . h ' .
        . U 7 .
. . . . . . . . . . . . . - . .
. . . . . . . . . . . . . & . .
. . . . . . / ! @ . . . . & . .
. . . . . . & . . . . . . & . .
        . . & .
        . . & .
        . . & .
        . . & .

h7'wq-!@    
h           no-op
 7          Push 7 to the stack
            Stack: [..., diff, 7]
  'wq       Push w to the stack and send it to
            the bottom. We don't care about it,
            so it's now part of the ellipsis.
            Stack: [..., diff, 7]
     -!     if (diff = 7):
       @        End the program

Discerning between the two 'l's

So, now we've we know that the input was an l, but we don't know which l. If it's the first, we need to push another l to the bottom of the stack, but if it's the second, we need to push an o. Remember we saved -3 to the bottom of the stack just before we pushed the first l? We can use that to separate the two branches.

        . . . .
        . . . .
        . . . .
        . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
6 t ? . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
        . . . .
        . . . . 
        . . . .
        . . . .

The stack starts as [..., -3 or 140, ...]

Explanation
6t?         
6t          Take the 6th item from the top and move
            it to the top (which is either -3 or 140)
  ?         If that's positive, turn right, otherwise,
            turn left

First 'l'

If this was the first 'l', we need to push another 'l'. To save bytes, we use the same characters as for the first 'l'. We can simplify the stack to [...]. Here's the relevant part of the cube, with no-ops replaced by ampersands.

        p > q '
        . . . .
        . . . .
        . . . .
' . q . . . . . . . . l . . . .
$ W W . . . . . . . . > & & & &
. . ? . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
        . . . .
        . . . .
        . . . .
        . . . .

The following instructions are executed:

$'pq'lq
$'          no-op
  pq        no-op
    'l      Push the character code of l
            Stack: [..., 108]
      q     Send it to the bottom
            Stack: [108, ...]

We're about to enter the read/write loop, so we're done with this branch.

Second 'l'

If the input was the second 'l' in 'hello', the IP turned right at the question mark. Once again, we can simplify the stack to [...] and the IP starts at ?, pointing south this time.

        . . . .
        . . . .
        . . . .
        . . . .
. . . . . . . . . . . . . . . .
. . . u _ q < o ' \ . . . . . .
. . ? . . . . . . & . . . . . .
. . & . . . . . . & . . . . . .
        . . . .
        & & & &
        . . . .
        . . . .

The instructions executed are:

'oq_
'o          Push the character code of 'o'
            Stack: [..., 111]
  q         Move the top item to the bottom
            Stack: [111, ...]
   _        No-op

And the IP is about to enter the read/write loop again, so we're done with this branch as well.

\$\endgroup\$
1
  • \$\begingroup\$ An heroic effort! \$\endgroup\$
    – user9206
    Apr 18, 2017 at 5:54
6
\$\begingroup\$

Haskell, 41 47 43 bytes

f l|w@"hello"<-take 5l=w|a:b<-l=a:f b|1<2=l

Haskell's laziness handles the infinite input/output quit well.

Try it online!

Edit: didn't handle finite input - fixed. Thanks @Leo for pointing out.

Edit II: @Ørjan Johansen saved 4 bytes. Thanks!

\$\endgroup\$
4
  • 2
    \$\begingroup\$ The input may also be finite, so I think you need to deal with the case when you reach the end of the string \$\endgroup\$
    – Leo
    Apr 4, 2017 at 19:40
  • \$\begingroup\$ @Leo: Oops, totally missed it. Fixed. \$\endgroup\$
    – nimi
    Apr 4, 2017 at 20:03
  • 2
    \$\begingroup\$ The first guard can be shortened to |w@"hello"<-take 5l=w. \$\endgroup\$ Apr 17, 2017 at 23:56
  • \$\begingroup\$ @ØrjanJohansen: oh, that's a nice one. Thanks! \$\endgroup\$
    – nimi
    Apr 18, 2017 at 14:29
5
\$\begingroup\$

C++, 142 141 bytes

#import<iostream>
void f(std::istream&i){i>>std::noskipws;char c;for(std::string s="     ";s!="hello"&&i>>c;)s.erase(0,1),s+=c,std::cout<<c;}

Try it online!

\$\endgroup\$
5
  • \$\begingroup\$ Would this be possible with GCC? I don't see #import in GCC C++ programs... \$\endgroup\$
    – ckjbgames
    Apr 4, 2017 at 17:08
  • 1
    \$\begingroup\$ @ckjbgames #import is a deprecated GCC extension. \$\endgroup\$
    – Steadybox
    Apr 4, 2017 at 17:44
  • 1
    \$\begingroup\$ @ckjbgames more info here: stackoverflow.com/questions/172262/… \$\endgroup\$
    – iFreilicht
    Apr 5, 2017 at 12:05
  • \$\begingroup\$ @iFreilicht That question actually made me ask that. \$\endgroup\$
    – ckjbgames
    Apr 5, 2017 at 19:09
  • 1
    \$\begingroup\$ @ckjbgames you might want to take a look at the second answer: stackoverflow.com/a/172264/2533467 "The import in gcc is different from the import in VC++. It is a simple way to include a header at most once only." \$\endgroup\$
    – iFreilicht
    Apr 6, 2017 at 13:33
3
\$\begingroup\$

Node, 124 bytes

with(process)with(stdin)on('data',d=>[...d].map(c=>(s=(stdout.write(c),s+c).slice(-5))=='hello'&&exit()),setEncoding(),s='')

Not assuming that the stream will fit in available memory.

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

C#, 134 bytes

using C=System.Console;class P{static void Main(){var s="";for(int c;(c=C.Read())>=0&!s.Contains("olleh");C.Write(s[0]))s=(char)c+s;}}

Try It Online

Reads a character, checks it isn't -1 (EOS) and that we havn't seen "hello" yet, then prepends it to a string, and writes the character out. We prepend because s[0] is a lot shorter than (char)s. This has a quadratic cost in the length of the string, as it has to allocate and scan the entire input each time it reads a character (this will crash after 2GB of input due to constraints in the CLR, is that allowed?)

using C=System.Console;

class P
{
    static void Main()
    {
        var s="";
        for(int c;(c=C.Read())>=0&!s.Contains("olleh");C.Write(s[0]))
            s=(char)c+s;
    }
}

For a (longer: 142 bytes) version which won't run out of memory, and which has constant per-character cost, see below:

using C=System.Console;class P{static void Main(){var s="     ";for(int c;(c=C.Read())>=0&s!="hello";C.Write(s[4]))s=s.Substring(1)+(char)c;}}

This one keeps the last 5 characters in a 5-length string, which means short comparisons, and cheap last-char lookup, but is considerably more expensive to update.

using C=System.Console;

class P
{
    static void Main()
    {
        var s="     ";
        for(int c;(c=C.Read())>=0&s!="hello";C.Write(s[4]))
            s=s.Substring(1)+(char)c;
    }
}
\$\endgroup\$
3
\$\begingroup\$

PHP, 57 55 53 bytes

while(hello!=$s=substr($s.$c,-5))echo$c=fgetc(STDIN);

as there are no infinite files, I take input from STDIN. Run with -nr.

Loop through input, print the current character, append it to $s, cut $s to the last 5 characters. Break loop when $s is hello.

\$\endgroup\$
3
\$\begingroup\$

Vim, 39 bytes

:im hello hello:se noma
:map : i

i

Try it online!

:im hello                        "Remap 'hello' in insert mode to
          hello                "write hello, then hit escape
                 :se noma       "then set the buffer to not-modifiable
:map : i                        "THEN remap ':' to 'i' so that can't be changed

i                                "enter insert mode and await an infinite stream of input
\$\endgroup\$
4
  • \$\begingroup\$ Is this an accepted input method for Vim? I thought Vim programs usually expect the input to be already in the buffer before they start. \$\endgroup\$ Apr 4, 2017 at 19:17
  • \$\begingroup\$ To be honest I don't know? That's true, but hardly allows for an infinite stream, so I just did it this way without really thinking that hard about it. \$\endgroup\$
    – nmjcman101
    Apr 4, 2017 at 19:36
  • \$\begingroup\$ What happens if there is an escape character in the input stream? \$\endgroup\$
    – dim
    Apr 7, 2017 at 7:04
  • \$\begingroup\$ @dim I asked, and OP specified only printable ASCII and newlines. ESC is not included in printable ASCII afaik \$\endgroup\$
    – nmjcman101
    Apr 10, 2017 at 2:30
3
\$\begingroup\$

PowerShell, 111 bytes

There is probably a better way to do this, but I can't see it at the moment.

while(($x=($x+$host.UI.RawUI.ReadKey("IncludeKeyDown").character+"     ").substring(1,5)).CompareTo("hello")){}

This reads the key strokes without supressing the echo. The character is added to $x which is trimmed to the last 5 characters and compared to "hello". This carries on until the comparison is true.

Note: this does not work in the PowerShell ISE. ReadKey is disabled in that environment.

\$\endgroup\$
3
\$\begingroup\$

Scheme 115 bytes

(do((c(read-char)(read-char))(i 0(if(eqv? c(string-ref"hello"i))(+ i 1)0)))((or(eof-object? c)(= i 5)))(display c))

Readable version:

(do ((c (read-char) (read-char))                            ; read stdin
     (i 0 (if (eqv? c (string-ref "hello" i)) (+ i 1) 0)))  ; check target
    ((or (eof-object? c) (= i 5))) ; finish if end of stdin, or word found
  (display c))                     ; display each character

This takes an individual char from stdin each time around the loop, and marks off its position on the target word as it meets the characters of "hello".

Stops when the input runs out or "hello" has been seen. No memory used on infinite stream.

\$\endgroup\$
1
  • \$\begingroup\$ Cool answer, welcome to the site! \$\endgroup\$
    – DJMcMayhem
    Apr 5, 2017 at 16:53
3
\$\begingroup\$

AWK, 95 bytes

BEGIN{RS="(.)"
split("hello",h,"")}{for(j=0;++j<6;){c=RT
printf c
if(c!=h[j])next
getline}exit}

There are 2 things I learned here:
1) To split records between characters use RS="(.)" and then RT must be used instead of $1
2) ORS is used by print and is defaulted to "\n"
3) I can't count to 2 and using printf is "cheaper" than assigning ORS and using print

Example usage: Place code in FILE

awk -f FILE some_data_file

or

some process | awk -f FILE

Code was tested using Dennis's yes | ... suggestion and I saw lots and lots of ys.

FYI, you can do the RS assignment as an option and pull it out of the BEGIN block via:

awk -v RS='(.)'
\$\endgroup\$
2
  • \$\begingroup\$ Really tricky solution! (Maybe because it's Friday afternoon, but I find it good entry for obfuscated challenge too.) Though I would try a more awkish approach: BEGIN{RS="(.)"}{printf RT}"olleh"==a=RT substr(a,1,4){exit}. \$\endgroup\$
    – manatwork
    Apr 7, 2017 at 16:14
  • \$\begingroup\$ Oddly enough I have almost exactly answer ready to submit an hour ago... and forgot to submit it. :p \$\endgroup\$ Apr 7, 2017 at 16:36
3
\$\begingroup\$

Python 3 (Linux), 73 72 bytes

s=c='_';I=open(0)
while'olleh'!=s>''<c:c=I.read(1);s=c+s[print(end=c):4]

Thanks to @MitchSchwartz for golfing off 1 byte!

Try it online!

\$\endgroup\$
4
  • \$\begingroup\$ I don't understand. How does the condition for while evaluate properly? It looks like you're comparing a boolean to an empty string. \$\endgroup\$
    – iFreilicht
    Apr 6, 2017 at 15:07
  • 1
    \$\begingroup\$ s[print(end=c):4] saves a byte \$\endgroup\$ Apr 6, 2017 at 18:12
  • 1
    \$\begingroup\$ @iFreilicht Python parses chained conditionals as one would in math (a<b<c, for example). The condition is a shorthand for 'olleh'!=s and s>''and''<c). The middle test isn't needed, but chaining them is shorter than the straightforward 'olleh'!=s and''<c. \$\endgroup\$
    – Dennis
    Apr 7, 2017 at 16:49
  • \$\begingroup\$ @MitchSchwartz That it does. Thank you! \$\endgroup\$
    – Dennis
    Apr 7, 2017 at 16:51
3
\$\begingroup\$

Ruby, 59 49 48 43 bytes

Now rant-free, shorter, and without memory leak.

s=''
s=$>.putc$<.getc+s[0,4]until'olleh'==s

Saved 5 bytes by getting rid of some parentheses and a space thanks to Dennis

\$\endgroup\$
1
3
\$\begingroup\$

8086 machine code, 22 bytes

00000000  bf 11 01 b4 01 cd 21 ae  75 f6 81 ff 16 01 72 f3  |......!.u.....r.|
00000010  c3 68 65 6c 6c 6f                                 |.hello|
00000016

Equivalent assembly code:

org 0x100
use16
a:  mov di, msg
b:  mov ah, 1       ; read one byte from stdin with echo
    int 0x21        ; dos syscall -> result in AL
    scasb           ; if (DI++ == AL)
    jne a
    cmp di, msg+5
    jb b
    ret
msg db "hello"
\$\endgroup\$
2
  • \$\begingroup\$ How does it work? \$\endgroup\$
    – user9206
    Apr 17, 2017 at 6:08
  • 1
    \$\begingroup\$ I added the equivalent assembly code. It basically relies on one very useful DOS syscall, which reads one byte from stdin and echos it back to stdout at the same time. The 8086 also has a single-byte string comparison instruction which comes in handy here. \$\endgroup\$ Apr 17, 2017 at 16:34
2
\$\begingroup\$

Jelly, 24 bytes

“Ṣẉ»ẇ⁸Ṇȧ®
ṫ-3;ƈ©Ȯ¤µ⁺Ç¿ṛ“

Try it online!

Explanation:

ṫ-3;ƈ©Ȯ¤µ⁺Ç¿ṛ“ Main link. Arguments: 0
ṫ-3            Truncate the list to its 4 last elements.
   ;ƈ©Ȯ¤       Store a character from STDIN in the register, print it, and append it to the list (list is initially [0]).
        µ      Start a new monadic chain, everything to the left is a link.
          Ç    Execute the helper link with the existing list as its argument.
         ⁺ ¿   Do-while loop, left link is body, right link is condition.
            ṛ“ When the loop ends, replace the return value with [] (invisible on output).

“Ṣẉ»ẇ⁸Ṇȧ® Helper link. Arguments: string
“Ṣẉ»ẉ⁸Ṇ   Check if "hello" isn't in the string.
        ® Return the character we stored in the register.
       ȧ  Check if both of the above are truthy.
\$\endgroup\$
0
2
\$\begingroup\$

Pyth, 49 47 bytes

Wn"hello"=>5+kp$__import__("sys").stdin.read(1)

Pyth's not very good at taking a single character of input. Everything in $__import__("sys").stdin.read(1) is simply doing that. Also, it means that this only runs offline.

Everything else is short...

The program is a bodyless while loop. Inside the condition, the program reads a character, prints it back, appends that character to k (which is initially the empty string), trims off all but the last 5 characters of k, and then checks that the result is not "hello".

32 characters are getting one byte of input, 15 characters does the rest.

Tested on Linux, works even with no newline, infinite input, etc.

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

Lua, 68 64 bytes

l=""while l~="hello"do c=io.read(1)io.write(c)l=l:sub(-4)..c end
\$\endgroup\$
2
  • 1
    \$\begingroup\$ Change the slicing to l:sub(-4), then you can reduce the initialization of l="". \$\endgroup\$
    – manatwork
    Apr 5, 2017 at 16:31
  • \$\begingroup\$ @manatwork That's neat. Thanks for the tip. \$\endgroup\$
    – Blab
    Apr 5, 2017 at 21:02
1
\$\begingroup\$

Röda, 49 47 bytes

{a=[0]*5{|x|[x];a=a[1:]+x;z if[a&""="hello"]}_}

Try it online!

This is an anonymous function that reads characters from its input stream and outputs them until "hello" is found. It uses the array a to track the last characters.

It outputs some junk to STDERR, but I understood that is allowed.

Explanation:

{
    a=[0]*5                /* Initialize the array with 5 zeroes. */
    {|x|                   /* For each x in the input stream: */
        [x];               /* Print x */
        a=a[1:]+x;         /* Add x and remove the sixth last character. */
        z if[a&""="hello"] /* If "hello" is found, crash the program */
                           /* with an undefined variable. */
    }_                     /* End for loop. */
}
\$\endgroup\$
2
  • \$\begingroup\$ Where is the Roda documentation? \$\endgroup\$
    – ckjbgames
    Apr 4, 2017 at 17:10
  • \$\begingroup\$ @ckjbgames Here. I use the latest version 0.12, which is in its own branch in Github. \$\endgroup\$
    – fergusq
    Apr 4, 2017 at 17:17
1
\$\begingroup\$

Java 7, 122 118 124 123 150 141 bytes

void c()throws Exception{String a="aaaaa";for(int b;!a.equals("hello")&(b=System.in.read())>=0;a=a.substring(1)+(char)b)System.out.write(b);}

Now stops when the end of the stream is reached. Now handles infinite input without running out of memory.

\$\endgroup\$
3
  • \$\begingroup\$ I bet this can´t handle infinite input. \$\endgroup\$
    – Titus
    Apr 4, 2017 at 18:25
  • \$\begingroup\$ @Titus fixed... \$\endgroup\$
    – Poke
    Apr 4, 2017 at 19:06
  • \$\begingroup\$ I downvoted without seeing write being used instead of print. I can't undo my downvote, sorry for that :( \$\endgroup\$ Apr 5, 2017 at 9:52
1
\$\begingroup\$

Ruby, 51 bytes

x="";$><<x[-1]while/hello./!~x=x[/.{0,5}$/]+$<.getc
  • Does not expect newlines
  • Works with infinite input
\$\endgroup\$
1
\$\begingroup\$

AHK, 116 bytes

Loop,Read,%1%
{a=%A_LoopReadLine%`n
Loop,Parse,a
{Send % c:=A_LoopField
If((f:=c SubStr(f,1,4))=="olleh")
ExitApp
}}

There's nothing clever or magical in there, really. The variable %1% is the first passed argument and should be a file path with the stream. The file must be saved as it is updated but the code will read to the end even if it expands after the reading starts.

\$\endgroup\$
1
\$\begingroup\$

Mathematica, 107 bytes

i="";EventHandler[Dynamic@i,"KeyDown":>(i=i<>CurrentValue@"EventKey";If[StringTake[i,-5]=="hello",Exit[]])]

The output becomes a field where the user can infinitely type text (including newlines) until the last 5 characters are equal to "hello"; at that point, it exits.

\$\endgroup\$
1
\$\begingroup\$

brainfuck, 281 bytes

>++++++++[<+++++++++++++>-]>++++++++++[<++++++++++>-]<+>>+++++++++[<++++++++++++>-]>++++++++++[<+++++++++++>-]<+>+[[[[[,.<<<<[->>>>->+<<<<<]>>>>>[-<<<<<+>>>>>]<],.<<<[->>>->+<<<<]>>>>[-<<<<+>>>>]<],.<<[->>->+<<<]>>>[-<<<+>>>]<],.<<[->>->+<<<]>>>[-<<<+>>>]<],.<[->->+<<]>>[-<<+>>]<]

I'm not sure why, but I just felt like brainfuck was the right thing to do this. Doesn't require infinite memory, and can output forever.

Explained

Set up the buffers with helo
This is done Naively; sue me
>++++++++[<+++++++++++++>-]     h
>++++++++++[<++++++++++>-]<+>   e
>+++++++++[<++++++++++++>-]     l
>++++++++++[<+++++++++++>-]<+>  o

THE MAIN LOOP
+
[ matches o
    [ matches l
        [ matches l
            [ matches e
                [ matches h
                    ,. Read a character and immediently write it
                    <<<<[->>>>->+<<<<<] Subtract it from h
                    >>>>>[-<<<<<+>>>>>] Correct the h
                    < Terminate this part of the loop if it matches h
                ]
                ,. Same as above
                <<<[->>>->+<<<<] Subtract it from e
                >>>>[-<<<<+>>>>] Correct the e
                < Terminate this part of the loop if it matches e
            ]
            ,. Same as above
            <<[->>->+<<<] Subtract it from l
            >>>[-<<<+>>>] Correct the l
            < Terminate this part of the loop if it matches l
        ]
        ,. Same as above
        <<[->>->+<<<] Subtract it from l
        >>>[-<<<+>>>] Correct the l
        < Terminate this part of the loop if it matches l
    ]
    ,. Same as above
    <[->->+<<] Subtract it from o
    >>[-<<+>>] Correct the o
    < Terminate this part of the loop if it matches o
]

Try it online!

\$\endgroup\$
2
  • \$\begingroup\$ I was gonna do it like this but then I realized that this outputs the zero byte infinitly for input that doesn't contain "hello": tio.run/nexus/… \$\endgroup\$
    – KarlKastor
    Apr 7, 2017 at 7:18
  • \$\begingroup\$ This also fails on ahehellob. \$\endgroup\$ Apr 8, 2017 at 13:11

Your Answer

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