-
-
Save elimisteve/4442820 to your computer and use it in GitHub Desktop.
// 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) | |
} |
Finally figured this out in Rust! Note: I'm a total Rust n00b, so it's extremely likely that this could be made more elegant. The repetition of chan.clone()
is especially upsetting to my eyes, but it does the job:
extern mod std;
use task::spawn;
use pipes::{stream, Port, Chan, SharedChan};
// Purely functional doubler
// No need to combine computation / means of sending it in the same function
// In a real example, this could be an expensive computation
fn double(x: int) -> int {
x * 2
}
fn main() {
// Create port/channel pair for receiving/sending ints, respectively
let (port, chan): (Port<int>, Chan<int>) = stream();
// To allow multiple tasks to write, turn the channel into a SharedChan
let chan = SharedChan(move chan);
// Spawn 3 tasks to process data in the background,
// each writing to its own clone of the SharedChan
let child = chan.clone();
do spawn |move child| { child.send(double(10)); }
let child = chan.clone();
do spawn |move child| { child.send(double(20)); }
let child = chan.clone();
do spawn |move child| { child.send((|a: int, b: int| a+b)(30, 40)); }
// Make port/channel pair for the string result
let (result_port, result_chan): (Port<~str>, Chan<~str>) = stream();
do spawn |move port, move result_chan| {
let (x, y, z) = (port.recv(), port.recv(), port.recv());
let result: ~str = fmt!("%d + %d + %d = %d", x, y, z, x+y+z);
result_chan.send(move result);
}
// Receive and print result
io::println(result_port.recv());
}
Another Haskell solution, this one uses sparks and Control.Parallel
import Control.Parallel
main :: IO ()
main = putStrLn . show $ a `par` b `par` c `par` result `par` result
where double = (2 *)
a = double 10
b = double 20
c = 30 + 40
result = a + b + c
And running it:
./parallel +RTS -N2 -sstderr
130
55,188 bytes allocated in the heap
20 bytes copied during GC
45,692 bytes maximum residency (1 sample(s))
23,940 bytes maximum slop
2 MB total memory in use (0 MB lost due to fragmentation)
Tot time (elapsed) Avg pause Max pause
Gen 0 0 colls, 0 par 0.00s 0.00s 0.0000s 0.0000s
Gen 1 1 colls, 0 par 0.00s 0.00s 0.0001s 0.0001s
Parallel GC work balance: -nan% (serial 0%, perfect 100%)
TASKS: 4 (1 bound, 3 peak workers (3 total), using -N2)
SPARKS: 4 (0 converted, 0 overflowed, 0 dud, 0 GC'd, 4 fizzled)
INIT time 0.00s ( 0.00s elapsed)
MUT time 0.00s ( 0.00s elapsed)
GC time 0.00s ( 0.00s elapsed)
EXIT time 0.00s ( 0.00s elapsed)
Total time 0.00s ( 0.00s elapsed)
Alloc rate 596,150,106 bytes per MUT second
Productivity 87.6% of total user, 211.7% of total elapsed
Hmm, the Haskell versions don't seem to output what they're supposed to, which is the string
20 + 40 + 70 = 130
with a newline. @elimisteve's Go, @jeena's Erlang, @danielsz's Clojure and my Rust all print it like this and @anarazel's C is close but also prints "data: " in front. However, @jonte and @ocharles, your Haskell versions seem to only print 130.
I don't mean to bash @jeena's nice Erlang code, but here is my quick take on it:
-module (foo).
-compile (export_all).
collect_3(Target, [X, Y, Z] = L) ->
Target ! io_lib:format("~w + ~w + ~w = ~w~n", [Z, Y, X, lists:sum(L)]);
collect_3(Target, L) ->
receive V -> collect_3(Target, [V | L]) end.
main() ->
Collector = spawn(?MODULE, collect_3, [self(), []]),
spawn(fun() -> Collector ! 10 * 2 end),
spawn(fun() -> Collector ! 20 * 2 end),
spawn(fun() -> Collector ! 30 + 40 end),
receive X -> io:format(X) end.
Example:
$ erl
1> c(foo).
{ok,foo}
2> foo:main().
20 + 40 + 70 = 130
ok
3>
Here's another one with Clojure
(let [v (pmap #(eval %) '[(* 2 10)(* 2 20)(+ 30 40)])]
(println @(future (str (reduce #(str %1 "+" %2) v) "=" (reduce + v)))))
One line with bash!
(expr 10 \* 2 & expr 20 \* 2 & expr 30 \+ 40) | (read a; read b; read c; echo "\"$a + $b + $c = \"; $a + $b + $c" | bc)
@yoavrubin: the first line of your Clojure example can be made even more succint and clear:
(let [v (pvalues (* 2 10) (* 2 20) (+ 30 40))]
And using a function instead of let, it can also be made an one-liner:
(println @(future (#(str (apply str (interpose \+ %)) \= (apply + %)) (pvalues (* 2 10) (* 2 20) (+ 30 40)))))
When squeezed as much as possible (and loosing a clarity), it also fits into the one line of a gist comment:
(print@(future(#(str(apply str(interpose\+%))\=(apply +%))(pvalues(* 2 10)(* 2 20)(+ 30 40)))))
using System;
using System.Linq;
using System.Threading.Tasks;
class Program
{
static void Main(string[] args)
{
Console.WriteLine(
Task.WhenAll(
Task.Run(() => 10 * 2), Task.Run(() => 20 * 2), Task.Run(() => 30 + 40))
.ContinueWith(t => string.Join(" + ", t.Result) + " = " + t.Result.Sum()).Result
);
}
}
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...
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)
}
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)
}
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())
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));
}
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));
}
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.
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
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...
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
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))))
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.
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
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;
}
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
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)
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)
}
Challenge accepted :)
PHP: https://gist.github.com/polonskiy/6b21065008c21170d9d6
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);
}
@polonskiy Actually, I'm not even mad! That's amazing 😨
Would be interesting to see this done with http://libmill.org/ !
Just for the record, for those that prefer the printf idiom, you can do it like so when binding to
result
: