HelolW rdlo (un desafío de subprocesos)

39

Tengo un reto para usted:

  • Imprime "Hello World" en cualquier idioma.
  • Cada personaje debe imprimirse desde su propio hilo único.

Eso es. Obviamente, como no hay garantía de que los subprocesos funcionen en el orden en que los inicia, debe hacer que el subproceso de su programa sea seguro para garantizar que la salida se imprima en el orden correcto.

Y, como se trata de código de golf, gana el programa más corto.

Actualizar:

El ganador es la entrada APL de Marinus , con 34 caracteres. También gana el premio por la entrada menos legible.

Tharwen
fuente
10
Un mejor nombre para esto habría sidoHelolW rdlo
Cristian Lupascu
Ja, eso me gusta.
Cambiarlo de
Aww ... es demasiado corto
Tharwen
1
Es divertido ver cuántas personas ignoran la pista "obviamente, ya que no hay garantía de que los hilos operen en el orden en que los inicias" y piensan que lo hicieron bien.
Joa Ebert
Si bien es cierto que "no hay garantía de que los hilos operen en el orden en que los inicias" en la práctica, casi siempre lo harán para un programa tan trivial. Para evitar esta confusión, agregaría al problema que cada subproceso debe 1) esperar un número aleatorio (pequeño) de milisegundos 2) emitir su char 3) esperar otro tiempo aleatorio (tal vez largo) De esta manera la gente podría decir si el código funciona simplemente ejecutándolo un par de veces. Y las soluciones join () funcionarían mucho peor. Sin una espera aleatoria, una carrera exitosa podría inducir a error al pensar que su programa es correcto.
silviot

Respuestas:

10

APL (Dyalog) ( 44 43 39 34)

{⍞←⍺⊣⎕DL⍵}&⌿2 11⍴'Hello World',⍳11

Explicación:

  • 2 11⍴'Hello World',⍳11 crea una matriz: (H, 1), (e, 2), ...
  • &⌿ significa: para cada columna de la matriz, hacer en un hilo separado:
  • En un hilo, ahora es el personaje y ahora es el momento
  • ⎕DL⊃⍵espera por segundos
  • Luego, ⍞←⍺genera el carácter.
marinus
fuente
11
¿Sabes que? Tomaré tu palabra por ello ... :)
Refuerce
OK, este es el más corto. ¡Felicidades!
Tharwen
19

C, 61 62 caracteres

i;main(){write(1,"Hello World\n"+i++,1);i>13||fork()||main();}

Todas las funciones de la biblioteca pthread tienen muuuucho nombres, así que en su lugar, inicié un proceso completamente separado para cada personaje. fork()Es mucho más corto.

Era necesario usarlo en write()lugar de putchar()porque las funciones de almacenamiento en búfer stdio no son seguras para subprocesos.

Editado : Copia de seguridad de hasta 62 caracteres. En mi celo, bajar a 61 caracteres también dejó caer el hilo de seguridad.

caja de pan
fuente
Debería ser posible cambiar la instrucción de escritura write(1,"Hello World\n",!!++i)por 2 bytes. Buena solución de lo contrario.
primo
Deberías probar eso y ver qué produce.
breadbox
Mi error, quise decir!!++i
primo
Parece ser lo que escribiste la primera vez, así que no veo qué error estabas tratando de corregir. Y no estaba siendo gracioso: honestamente quería decir que deberías probarlo tú mismo y ver qué pasa. Al eliminar la adición, cada hilo imprimirá el primer carácter de la cadena.
breadbox
Originalmente había escrito !!i++, pero lo edité unos segundos después, porque me di cuenta de que se evaluaría 0en la primera iteración. Asumí que habías visto la versión sin editar. No puedo probar su código, porque solo imprime el primer carácter, una vez . Sin embargo, hay muchas alternativas; i++<13, usando !!i, o inclusowrite(1,"Hello World\n",i++>13||fork()||main())
primo
9

Ruby, 46 caracteres.

"Hello World".chars{|c|Thread.new{$><<c}.join}

Se sincroniza debido al hecho de que el programa espera a que finalice el subproceso antes de comenzar el siguiente subproceso y continuar con el siguiente carácter.

Howard
fuente
7

Pythonect (35 caracteres)

http://www.pythonect.org

"Hello World"|list|_.split()->print
Jonathan Rom
fuente
Este es el más corto hasta ahora. Como no tengo idea de lo que realmente hace, supondré que es correcto y lo aceptaré en un día o dos si nadie habla en contra o publica algo más corto.
Tharwen
1
Acabo de echar un vistazo a los ejemplos. ¿No debería la declaración de impresión o la declaración de lista tener corchetes [] alrededor?
Dalin Seivewright
1
Hola, estoy reenviando la respuesta de Itzik (creador de pythonect): '->' y '|' son ambos operadores Pythonect. El operador de tubería pasa un elemento a un elemento, mientras que el otro operador pasa todos los elementos a la vez. Lo que hace el programa anterior es, toma la cadena "Hello World", la convierte en una lista, divide la lista en caracteres y envía cada carácter para imprimir. Es posible optimizar el programa aún más, a lo siguiente: iter ("Hello World") | print Lo que hace es, itera la cadena "Hello World" y envía cada carácter a imprimir (de manera sincronizada / bloqueante). Saludos, Itzik Kotler | ikotler.org
Leon Fedotov
¿Cómo se hace el enhebrado aquí?
Rohit
1
Como mencionó @LeonFedotov, y desde la fuente pythonect (disponible en pythonect ) después del análisis, para cada iteración y el operador '->', el enhebrado se realiza así: thread = threading.Thread (target = __ run, args = ([((( operador, elemento)] + expresión [1:], copy.copy (globals_), copy.copy (locals_), return_value_queue, not iterate_literal_arrays)) thread.start ()
Jonathan Rom
6

Python ( 101 93 98)

Esta es la solución de Peter Taylor. Funciona retrasando la impresión del enésimo carácter en N segundos. Ver comentarios.

import sys.threading as t
for x in range(11):t.Timer(x,sys.stdout.write,"Hello World"[x]).start()

Este es el original:

import sys,threading as t
for x in "Hello World":t.Thread(None,sys.stdout.write,x,x).start()

Funcionó porque el tiempo que lleva imprimir un solo carácter es menor que el tiempo que tarda Python en inicializar un nuevo subproceso, por lo tanto, el subproceso N terminaría antes de que se creara el subproceso N + 1. Aparentemente es contrario a las reglas confiar en esto.

marinus
fuente
Puede guardar 3 caracteres cambiando import sys,threadinga import sys,threading as ty puede guardar 2 más, pasando los argumentos a Thread como argumentos posicionales, en lugar de argumentos de palabras clave.
Joel Cornett
2
¿Dónde está el código que se ocupa de la seguridad del hilo? Simplemente disparas hilos esperando que se ejecuten en el mismo orden en que los inicias. Esto no siempre será cierto y de hecho es la "parte difícil" de este problema. En lugar de optimizar el tamaño de su programa, debería volver a considerar el problema: no lo obtuvo en primer lugar. Consulte gist.github.com/2761278 para obtener una prueba de que este código no funciona.
silviot
Arreglo rapido. Usar en threading.Timerlugar de threading.Thread. Pase xcomo el parámetro de suspensión.
Joel Cornett
1
La sugerencia de Joel se puede mejorar en 4 afor x in range(11):t.Timer(x,sys.stdout.write,"Hello World"[x]).start()
Peter Taylor
1
@silviot: estaba explotando el hecho de que la creación de subprocesos implica la creación de instancias de un objeto y, por lo tanto, toma de uno a dos tercios de un milisegundo en los sistemas que he probado. La salida de caracteres no tiene esta sobrecarga, tomando solo una décima parte de este tiempo. Por lo tanto, "siempre" funcionará, siempre y cuando no anule nada. Stdout está protegido, por lo que tampoco debería dar problemas.
Marinus
4

C # 73

"hello world".ToList().ForEach(c=>Task.Run(()=>Console.Write(c)).Wait());
JJoos
fuente
No estoy seguro de que esto satisfaga el requisito de que cada letra se imprima a través de su propio hilo, ya que el tpl puede reutilizar hilos.
statichippo
En teoría tiene razón, pero en mi PC ThreadPool.GetMaxThreads o ThreadPool.GetAvailableThreads devuelve un valor de alrededor de 1000 para los hilos de trabajo y de E / S.
JJoos
4

APL (Dyalog Unicode) , SBCS de 28 bytes

Programa completo Imprime en stderr. Inspirado en la solución de marinus .

'Hello World'{⍞←⍺⊣⎕DL⍵}&¨⍳11

Pruébalo en línea!

⍳11 primeros 11 enteros

'Hello World'{}&¨ Para cada entero como argumento derecho ( ), genera la siguiente función con el carácter correspondiente como argumento izquierdo ( ):

⎕DL⍵d e l segundos de argumento correcto

⍺⊣ descartar eso (la demora efectiva) a favor del carácter de argumento izquierdo

⍞← imprima eso en stdout sin salto de línea final

Adán
fuente
¿qué tal ⍞∘←&¨'dlroW olleH'? - No sé si se garantiza en teoría, pero parece que siempre se imprima en el orden correcto
NGN
@ngn Obviamente, como no hay garantía de que los subprocesos funcionarán en el orden en que los inicia, debe hacer que el subproceso de su programa sea seguro para garantizar que la salida se imprima en el orden correcto.
Adám
esa es la restricción que estaba tratando de abordar, podría estar garantizado. Ejecuté esto 100 veces y parece que el programador de subprocesos siempre recoge los subprocesos en orden inverso. O al menos ese es el caso cuando hay ≤11 tareas. AFAIK ⍞∘←no es interrumpible (¿o sí? ¿Tal vez puedas preguntarle a un desarrollador C?). Dyalog implementa hilos verdes: 1 hilo real que finge ser muchos, por lo que si no se puede producir un cambio de hilo (verde), el orden es predecible.
ngn
3

Java (160 caracteres)

class A{static int i;public static void main(String...a){new Thread(){public void run(){System.out.print("Hello World".charAt(i++));if(i<11)main();}}.start();}}

Sí, sé que este es el lenguaje incorrecto para el golf de código, lo hago por diversión.

Malcolm
fuente
class A{public static void main(String[]args){new B(0).start();}}class B extends Thread{int i;B(int j){i=j;}public void run(){System.out.print("Hello World".charAt(i));if(i<10)new B(i+1).start();}}-197 caracteres
Príncipe John Wesley
@Prince Sí, gracias por la corrección!
Malcolm
class A extends Thread{static int i;public static void main(String[]args){System.out.print("Hello World".charAt(i++));if(i<11)new A().start();}public void run(){main(null);}}- 174 caracteres
Wouter Coekaerts
@Wouter ¡Muy buena! Lo extrañé totalmente.
Malcolm
1
@ Malcolm, @ bkil, @ Wouter: class A{static int i;public static void main(String...a){new Thread(){public void run(){System.out.print("Hello World".charAt(i++));if(i<11)main();}}.start();}}- 160 caracteres
Príncipe John Wesley
2

Golpe (64)

:(){ [ "$1" ]&&(echo -n "${1:0:1}"&: "${1:1}")};: "Hello World"
marinus
fuente
@marinus Golfed doen por 3 caracteres::()([ "$1" ]&&(printf "${1:0:1}"&: "${1:1}"));: Hello\ World
Digital Trauma
@fossilet Funciona para mí en Linux y OSX, varias versiones de bash. El último uno o dos caracteres a veces se imprime después de la solicitud del shell.
Trauma digital
2

Haskell ( 120 118)

import Control.Concurrent
t=threadDelay.(*3^8)
main=(\(x,y)->forkIO$t x>>putChar y)`mapM_`zip[0..]"Hello World">>t 99

No estoy tan seguro de multiplicar por 9999: tengo un Xeon de 2Ghz en el que funcionará bien incluso si no lo haces, pero también tengo un Pentium 4 que lo necesita (999 dio una salida confusa y 99 no No hagas nada en absoluto.)

marinus
fuente
Guarde 2 caracteres usando en (*5^6)lugar de (*9999)y sin usar comillas inversas para mapM_.
Caracol mecánico
@Mechanicalsnail Si elimina los backticks, necesita un par adicional de llaves, de lo contrario, se analiza como (((mapM_ (\(x,y) ... )) zip) [0..]) ...lo que no desea.
Marinus
En cuanto a 999, podría truncarse a 0 debido a limitaciones del sistema operativo, pero podría estar equivocado. ¿Qué sistema operativo estás usando?
Joey Adams
2

scala ( 81 79 caracteres)

def?(l:String){if(l.size!=0)new Thread{print(l(0));?(l.tail)}}
?("Hello World")
Príncipe John Wesley
fuente
Un poco más corto: def? (L: String) {if (l.size> 0) new Thread {print (l (0));? (L.tail)}}
Joa Ebert
@JoaEbert: sí, agradable
Príncipe John Wesley
2

Groovy, 51 personajes

"Hello World".each{a->Thread.start{print a}.join()}
Gregor Petrin
fuente
1

D (135 caracteres)

import std.concurrency,std.stdio;
void main(){
    p("Hello World");
}
void p(string h){
    write(h[0]);
    if(h.length>1)spawn(&p,h[1..$]);
}

Solo comienzo el siguiente hilo cuando ya he impreso el carácter actual

editar +2 caracteres para una mejor verificación encuadernada

monstruo de
fuente
Me sale un [email protected](6): Range violationerror
Fish Monitor
@fossilet lo arreglé
monstruo de trinquete
1

Scala 74

"Hello World".zipWithIndex.par.map(x=>{Thread.sleep(x._2*99);print(x._1)})
  • zipWithIndex produce ((H, 0), (e, 1), (l, 2) ...).
  • par lo convierte en una colección paralela.

Pruebas:

(1 to 10).foreach {_ => "Hello World".zipWithIndex.par.map(x=>{Thread.sleep(x._2*99);print(x._1)});println()}
Hello World
Hello World
Hello World
...
Hello World
usuario desconocido
fuente
scala> "Hello World".zipWithIndex.par.foreach(x=>{Thread.sleep(x._2*99);print(x._1)}) Hel lWrolod- Tengo esto
Príncipe John Wesley
También println(Thread.currentThread.getName)muestra que los hilos no son únicos.
Príncipe John Wesley
@PrinceJohnWesley: Supongo que necesitas un núcleo por letra, por lo que el par distribuirá el trabajo en todos los núcleos.
usuario desconocido
Entiendo. entonces se requiere un núcleo por letra + alta resolución del reloj del sistema.
Príncipe John Wesley
usar en maplugar de foreach. Puedes guardar 4 caracteres.
Príncipe John Wesley
1

Javascript (72)

(function f(){console.log("Hello world"[i++]);i<11&&setTimeout(f)})(i=0)
Eric
fuente
1

Scala (45)

Hilo # unirse a la solución basada

"Hello World"map{x=>new Thread{print(x)}join}

o

for(x<-"Hello World")new Thread{print(x)}join
Príncipe John Wesley
fuente
1

Este es mi intento de F #. Mi primer programa serio de F #. Por favor se amable.

let myprint c = async {
        printfn "%c"c
}
"Hello World"|>Seq.map myprint|>Async.Parallel|>Async.RunSynchronously|>ignore
Smetad Anarkist
fuente
0

Ir

package main
import"fmt"
func main(){o:=make(chan int)
for _,c:=range"Hello World"{go func(c rune){fmt.Printf("%c",c)
o<-0}(c)}
for i:=0;i<11;i++{<-o}}
efímero
fuente
No necesita contar los personajes, ¡lo haremos por usted!
usuario desconocido
0

Erlang (90)

-module(h).
r()->r("Hello World").
r([])->'';r([H|T])->spawn(h,r,[T]),io:format("~c",[H]).

Compilar erlc +export_all h.erl

Nik
fuente
0

Nimrod, 121

proc p(c:char){.thread.}=write(stdout,c)
for c in "Hello World":
 var t:TThread[char]
 createThread(t,p,c)
 joinThread(t)
dejó de girar en sentido antihorario
fuente
0

Python: demasiados caracteres, pero funciona.

# Ok. First we patch Threading.start to test wether our solution actually works

import threading
import random, time
original_run = threading.Thread.run


def myRun(self):
    tosleep = random.randint(0,200)/1000.0
    time.sleep(tosleep)
    original_run(self)

threading.Thread.run = myRun

# And now the real code:
import time, sys, threading
string_to_write = "Hello World\n"
current_char_index = 0 # This integer represents the index of the next char to be written
# It will act as a semaphore: threads will wait until it reaches
# the index of the single char that particular thread is due to output

class Writer(threading.Thread):
    def __init__(self, char_to_write, index_to_write):
        self.char_to_write, self.index_to_write = char_to_write, index_to_write
        super(Writer, self).__init__()
    def run(self):
        ch = globals()['current_char_index']
        while not self.index_to_write == ch:
            time.sleep(0.005)
        sys.stdout.write(self.char_to_write)
        # This will be atomic because no other thread will touch it while it has "our" index
        globals()['current_char_index'] += 1

for i, char in enumerate(string_to_write):
    Writer(char, i).start()
silviot
fuente
No entiendo cuál es el propósito de aleatorizar el tiempo de sueño.
Joel Cornett
@Joel para asegurarse de que, cuando funciona, no es una coincidencia afortunada que los hilos se ejecuten en el mismo orden en que fueron disparados.
silviot
0

C # 90 84

foreach(var c in"Hello World"){var t=new Thread(Console.Write);t.Start(c);t.Join();}

Versión en ejecución: http://ideone.com/0dXNw

Cristian Lupascu
fuente
console.write es muy rápido, por eso esto puede funcionar para usted, ¡pero definitivamente no es seguro para subprocesos!
statichippo
@statichippo tienes razón; Lo arreglé
Cristian Lupascu
0

Objetivo-C (183 caracteres)

-(void) thread {[self performSelectorInBackground:@selector(printC) withObject:nil];}
-(void) printC {char *c = "Hello World"; for(int i = 0; c[i] != '\0'; i++) {printf("%c", c[i]);}}
Mergesort
fuente
0

Haskell 99 Personajes

import Control.Concurrent
f[]=return()
f(x:xs)=(putChar x)>>(forkIO$f xs)>>f[]
main=f"Hello World"

La forma en que funciona es que cada subproceso comienza el siguiente después de haber mostrado su carácter, por lo que las cosas realmente útiles no pueden suceder fuera de secuencia.

PyRulez
fuente
0

Bash , 43 bytes

xargs -n1 printf<<<'H e l l o \  W o r l d'

Pruébalo en línea!

xargs se bifurca por separado printf proceso para cada personaje (y espera a que salga).

Bash , 45 bytes, sin utilidades externas

eval \(printf\ {H,e,l,l,o,\\\ ,W,o,r,l,d}\)\;

Pruébalo en línea!

Se expande a (printf H); (printf e); (printf l); (printf l); (printf o); (printf \ ); (printf W); (printf o); (printf r); (printf l); (printf d);antes de la evaluación. Los paréntesis hacen que Bash se bifurque en un subshell para cada letra (y espere a que salga), pero esta vez printfes el Bash incorporado.

Anders Kaseorg
fuente