Skip to content

Instantly share code, notes, and snippets.

@elimisteve
Last active February 18, 2024 01:52
Show Gist options
  • Save elimisteve/4442820 to your computer and use it in GitHub Desktop.
Save elimisteve/4442820 to your computer and use it in GitHub Desktop.
Programming Challenge: Launch 4 threads, goroutines, coroutines, or whatever your language uses for concurrency, in addition to the main thread. In the first 3, add numbers together (see sample code below) and pass the results to the 4th thread. That 4th thread should receive the 3 results, add the numbers together, format the results as a strin…
// Steve Phillips / elimisteve
// 2013.01.03
// Programming Challenge: Launch 4 threads, goroutines, coroutines, or whatever your language uses for concurrency,
// in addition to the main thread. In the first 3, add numbers together (see sample code below) and pass the results
// to the 4th thread. That 4th thread should receive the 3 results, add the numbers together, format the results as
// a string (see sample code), and pass the result back to `main` to be printed.
//
// Do this as succinctly and readably as possible. _Go!_ #golang #programming #concurrency #challenge
package main
import "fmt"
// intDoubler doubles the given int, then sends it through the given channel
func intDoubler(ch chan int, n int) {
ch <- n*2
}
func main() {
// Make channel of ints
ch := make(chan int)
answer := make(chan string)
// Spawn 3 goroutines (basically threads) to process data in background
go intDoubler(ch, 10)
go intDoubler(ch, 20)
go func(a, b int) { ch <- a+b }(30, 40) // Take 2 ints, write sum to `ch`
// Create anonymous function on the fly, launch as goroutine!
go func() {
// Save the 3 values passed through the channel as x, y, and z
x, y, z := <-ch, <-ch, <-ch
// Calculate answer, write to `answer` channel
answer <- fmt.Sprintf("%d + %d + %d = %d", x, y, z, x+y+z)
}()
// Print answer resulting from channel read
fmt.Printf("%s\n", <-answer)
}
@elimisteve
Copy link
Author

Awesome guys, these are great.

@nizox Other than using ampersands to background processes, I didn't know it was so easy to do asynchronous programming at the command line. Sweet.

@aeonshifter I can see why using C# is much nicer than Java.

Looks like I'm gonna have to step up my game and make my Go example more succinct...

@elimisteve
Copy link
Author

package main

import "fmt"

func main() {
    ch := make(chan int)
    answer := make(chan string)

    intDoubler := func(n int) { ch <- n*2 }
    go intDoubler(10)
    go intDoubler(20)
    go func(a, b int) { ch <- a+b }(30, 40)

    go func() {
        x, y, z := <-ch, <-ch, <-ch
        answer <- fmt.Sprintf("%d + %d + %d = %d", x, y, z, x+y+z)
    }()

    fmt.Printf("%s\n", <-answer)
}

@elimisteve
Copy link
Author

Bending the rules a bit, similar to other examples...

package main

func main() {
    ch := make(chan int)

    go func(a int)    { ch <- a*2 }(10)
    go func(a int)    { ch <- a*2 }(20)
    go func(a, b int) { ch <- a+b }(30, 40)

    x, y, z := <-ch, <-ch, <-ch
    println(x, "+", y, "+", z, "=", x+y+z)
}

@gholms
Copy link

gholms commented Jan 8, 2013

I'm noticing a distinct lack of python here. :P

from concurrent.futures import Future, ThreadPoolExecutor

def fmt_sum(*int_futures):
    ints = tuple(map(Future.result, int_futures))
    return ' + '.join(map(str, ints)) + ' = ' + str(sum(ints))

with ThreadPoolExecutor(max_workers=4) as tp:
    a = tp.submit(lambda x: x * 2, 10)
    b = tp.submit(lambda x: x * 2, 20)
    c = tp.submit(lambda x, y: x + y, 30, 40)
    d = tp.submit(fmt_sum, a, b, c)

print(d.result())

@bstrie
Copy link

bstrie commented Jan 8, 2013

Here's an update of the Rust solution:

use task::spawn;

fn main() {
    let (port, chan) = pipes::stream();
    let chan = pipes::SharedChan(chan);

    let (s1, s2, s3) = (chan.clone(), chan.clone(), chan.clone());
    do spawn { s1.send( (|a: int| a*2)(10) ) }
    do spawn { s2.send( (|a: int| a*2)(20) ) }
    do spawn { s3.send( (|a: int, b: int| a+b)(30, 40) ) }

    let (x, y, z) = (port.recv(), port.recv(), port.recv());
    io::println(fmt!("%d + %d + %d = %d", x, y, z, x+y+z));
}

@bstrie
Copy link

bstrie commented Jan 8, 2013

Oh hey, turns out Rust has a futures library after all:

extern mod std;
use std::future::spawn;

fn main() {
    let p1 = do spawn { (|a: int| a*2)(10) },
        p2 = do spawn { (|a: int| a*2)(20) },
        p3 = do spawn { (|a: int, b: int| a+b)(30, 40) };

    let (x, y, z) = (p1.get(), p2.get(), p3.get());
    io::println(fmt!("%d + %d + %d = %d", x, y, z, x+y+z));
}

@rntz
Copy link

rntz commented Jan 8, 2013

A shorter (but still readable) Haskell solution using ForkIO. Also, formatted as requested.

import Control.Concurrent
import Data.List (intercalate)

main = do
  ch <- newChan
  answer <- newEmptyMVar
  mapM (\e -> forkIO $ seq e $ writeChan ch e)
       [10 * 2, 20 * 2, 30 + 40]
  forkIO $ do nums <- mapM readChan [ch,ch,ch]
              let ans = intercalate " + " (map show nums)
                        ++ " = " ++ show (sum nums)
              seq ans $ putMVar answer ans
  putStrLn =<< takeMVar answer

Haskell's laziness has to be carefully considered to ensure that the multiplications & additions run on the right thread. forkIO isn't really designed for scheduling pure computations onto multiple threads; par is probably a better choice here. I checked the above using Debug.Trace and myThreadId, and it seems to do the right thing.

@charl
Copy link

charl commented Jan 9, 2013

Ruby (works in MRI, RBX and Jruby):

require "rubygems"
require "celluloid"

ws = []
times_two = lambda { |x| x * 2 }
ws << Celluloid::Future.new { times_two.call(10) }
ws << Celluloid::Future.new { times_two.call(20) }
ws << Celluloid::Future.new { lambda { |x,y| x + y }.call(30, 40) }
r = Celluloid::Future.new do
  vs = ws.map { |w| w.value }
  "#{vs.join(" + ")} = #{vs.reduce(:+)}"
end

puts r.value

@tzach
Copy link

tzach commented Jan 9, 2013

Slight variation on Yoav (excellent) Clojure solution, replacing the eval with pcalls to functions.

(let [v (pcalls #(* 2 10) #(* 2 20) #(+ 30 40))] 
   (println @(future (str (reduce #(str %1 "+" %2) v) "=" (reduce + v)))))

Just notice @mnicky had even better solution with pvalues...

Copy link

ghost commented Jan 10, 2013

Mozart/Oz:

functor
import
    Application System
define X Y Z S in
    thread X =  2 * 10 end
    thread Y =  2 * 20 end
    thread Z = 30 + 40 end

    thread S = X#" + "#Y#" + "#Z#" = "#(X + Y + Z) end

    {System.showInfo S}
    {Application.exit 0}
end

@mikera
Copy link

mikera commented Jan 16, 2013

Another Clojure example, this time with a bit of macro meta-programming to give us a beautiful syntax:

(defmacro go [& body] `@(future ~@body))

(go (+ (go (* 2 10)) 
       (go (* 2 20))
       (go (+ 30 40))))

@velvia
Copy link

velvia commented Jan 24, 2013

You're really missing a Scala example.

import concurrent.Future
  for (a <- Future { 2 * 10 };
       b <- Future { 2 * 20 };
       c <- Future { 30 + 40 };
       sum <- Future { a + b + c })
  { println(s"$a + $b + $c = $sum") }

That's pretty readable.

@velvia
Copy link

velvia commented Jan 24, 2013

And the console output:

scala> :paste
// Entering paste mode (ctrl-D to finish)

  for (a <- Future { 2 * 10 };
        b <- Future { 2 * 20 };
        c <- Future { 30 + 40 };
        sum <- Future { a + b + c })
  { println(s"$a + $b + $c = $sum") }

// Exiting paste mode, now interpreting.


scala> 20 + 40 + 70 = 130

@vsrinivas
Copy link

C, written in an archaic style, likely would work on an early UNIX system.

#include <sys/wait.h>

#include <stdio.h>
#include <unistd.h>

int intDoubler(i)
     int i;
{
        return i * 2;
}

int intAdder2(i, j)
     int i, j;
{
        return i + j;
}

int intAdder3(i, j, k)
     int i, j, k;
{
        return i + j + k;
}

int main(argc, argv)
     int argc;
     char *argv[];
{
        int answer;
        int i, j, k;

        switch (fork()) {
        case 0:
                switch (fork()) {
                case 0:
                        return intDoubler(10);
                default:
                        break;
                }

                switch (fork()) {
                case 0:
                        return intDoubler(20);
                default:
                        break;
                }

                switch (fork()) {
                case 0:
                        return intAdder2(30, 40);
                default:
                        break;
                }

                wait(&i);
                wait(&j);
                wait(&k);
                return intAdder3(WEXITSTATUS(i), WEXITSTATUS(j), WEXITSTATUS(k));
        default:
                wait(&answer);
                printf("%d\n", WEXITSTATUS(answer));
                break;
        }

        return 0;
}

@coreyoconnor
Copy link

Here is nearly verbatim copy of the go source into haskell:

import Control.Monad
import Control.Concurrent
import Control.Concurrent.Chan

import Text.Printf

double :: Chan Int -> Int -> IO ()
double ch n = writeChan ch (n*2)

main = do
    ch <- newChan
    answer <- newChan

    forkIO $ double ch 10
    forkIO $ double ch 20
    forkIO $ (\a b -> writeChan ch (a + b)) 30 40
    forkIO $ do
        [x, y, z] <- replicateM 3 $ readChan ch
        writeChan answer $ (printf "%d + %d + %d = %d" x y z (x+y+z) :: String)

    printf "%s\n" =<< readChan answer

@robinp
Copy link

robinp commented Nov 16, 2013

Haskell, using monad-par:

module Main where

import Control.Monad
import Control.Monad.Par
import Text.Printf

-- | some common boilerplate
defer :: (NFData a) => Par a -> Par (Par a)
defer = liftM get . spawn
deferP :: (NFData a) => a -> Par (Par a)
deferP = liftM get . spawnP

calc :: Par String
calc = do
  pa <- deferP (2 * 10 :: Int)
  pb <- deferP (2 * 20)
  pc <- deferP (30 + 40)
  -- pc <- defer $ liftM2 (+) pa pb  -- this is a mildly more exiting use case
  liftM3 summary pa pb pc
  where
    summary a b c = printf "%d + %d + %d = %d" a b c (a+b+c)

main = do
  print (runPar calc)

@geraldstanje
Copy link

hi is the following a valid solution?

package main

import "fmt"
import "strconv"

/*
Programming Challenge: Launch 4 threads, goroutines, coroutines, or whatever your language uses for concurrency,
in addition to the main thread. In the first 3, add numbers together (see sample code below) and pass the results
to the 4th thread. That 4th thread should receive the 3 results, add the numbers together, format the results as a
string (see sample code), and pass the result back to main to be printed. Do this as succinctly and readably as
possible. Go! #golang #programming #concurrency #challenge
*/

func intToString(input_num int) string {
return strconv.FormatInt(int64(input_num), 10)
}

func intDoubler(a int, res chan int) {
res <- a * 2
}

func addInts(a int, b int, res chan int) {
res <- a + b
}

func formatFinal(res chan int, final chan string) {
var sum int

for i := 0; i<3; i++ {
    sum += <-res
}

final <- intToString(sum)

}

func main() {
intChan := make(chan int, 3)
strChan := make(chan string)

go intDoubler(10, intChan)
go intDoubler(20, intChan)
go addInts(30, 40, intChan)
go formatFinal(intChan, strChan)

res := <-strChan

fmt.Println(res)

}

@polonskiy
Copy link

@timhuff
Copy link

timhuff commented Sep 28, 2015

Rust 1.3 solution (looks like the earlier Rust solutions are outdated)

use std::thread;

fn main() {
    let mut children = vec![];
    children.push(thread::spawn(move || {10*2}));
    children.push(thread::spawn(move || {20*2}));
    children.push(thread::spawn(move || {30+40}));

    let adder = thread::spawn(move || {
        let mut sum = 0;
        let result = children.into_iter().map(|child| {
            let value = child.join().unwrap();
            sum += value;
            format!("{}", value)
        }).collect::<Vec<_>>().join(" + ");
        return format!("{} = {}", result, sum);
    });

    let result = adder.join().unwrap();
    println!("{}", result);
}

@Zizaco
Copy link

Zizaco commented Jan 12, 2016

@polonskiy Actually, I'm not even mad! That's amazing 😨

@elimisteve
Copy link
Author

Would be interesting to see this done with http://libmill.org/ !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment