HelolW rdlo (Поток задач)


39

У меня есть вызов для вас:

  • Напечатайте «Hello World» на любом языке.
  • Каждый символ должен быть напечатан из его собственной, уникальной нити

Вот и все. Очевидно, что поскольку нет никакой гарантии, что потоки будут работать в том порядке, в котором вы их запускаете, вы должны сделать поток вашей программы безопасным, чтобы вывод печатался в правильном порядке.

И, поскольку это кодовый гольф, выигрывает самая короткая программа.

Обновить:

Победителем является запись APL Маринуса , в 34 символов. Он также выигрывает приз за наименее читаемую запись.


10
Лучшее название для этого было быHelolW rdlo
Кристиан Лупаску

Ха, мне это нравится.
Меняем

Ой ... это слишком коротко
Тарвен

1
Забавно видеть, как многие люди игнорируют подсказку «очевидно, поскольку нет гарантии, что потоки будут работать в том порядке, в котором вы их запускаете», и думаете, что они поняли это правильно.
Джоа Эберт

Хотя это правда, что «нет никакой гарантии, что потоки будут работать в том порядке, в котором вы их запускаете», на практике они почти всегда будут работать для такой тривиальной программы. Чтобы избежать этой путаницы, я бы добавил к проблеме, что каждый поток должен: 1) ждать (маленькое) случайное число миллисекунд 2) выводить свой символ 3) ждать другое случайное (может быть, большое) количество времени таким образом, чтобы люди могли определить, код работает, просто запустив его пару раз. И решения join () будут работать намного хуже. Без случайного ожидания можно было бы сбить с толку при успешном запуске, чтобы думать, что его программа верна.
Сильвиот

Ответы:


10

APL (Dyalog) ( 44 43 39 34)

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

Объяснение:

  • 2 11⍴'Hello World',⍳11 создает матрицу: (H, 1), (e, 2), ...
  • &⌿ означает: для каждого столбца матрицы сделать в отдельном потоке:
  • В теме, теперь персонаж и сейчас время
  • ⎕DL⊃⍵ждет секунд.
  • Затем ⍞←⍺выводит символ.

11
Знаешь что? Я верю вашему слову ... :)
Bolster

ОК, это самое короткое Поздравляем!
Тарвен

19

C 61 62 символа

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

Все функции библиотеки pthread имеют более чем одинаковые имена, поэтому вместо этого я запустил отдельный процесс для каждого символа. fork()намного короче.

Пришлось использовать write()вместо того, putchar()потому что функции буферизации stdio не являются поточно-ориентированными.

Отредактировано : резервное копирование до 62 символов. В моем рвении снижение до 61 символа также понизило безопасность потока.


Должно быть возможным изменить оператор записи write(1,"Hello World\n",!!++i)на 2 байта. Хорошее решение в противном случае.
Примо

Вы должны попробовать это и посмотреть, что это дает.
хлебница

Свою ошибку я имел ввиду!!++i
primo

Похоже, это то, что вы написали в первый раз, поэтому я не вижу, какую ошибку вы пытались исправить. И я не был шутливым: я честно имел в виду, что вы должны попробовать это сами и посмотреть, что произойдет. При удалении дополнения каждый поток просто напечатает первый символ строки.
хлебница

Первоначально я написал !!i++, но отредактировал его несколько секунд спустя, потому что я понял, что он будет оценен 0на первой итерации. Я предположил, что вы видели неотредактированную версию. Я не могу проверить ваш код, потому что он печатает только самый первый символ, один раз . Есть много альтернатив, хотя; i++<13, используя !!iили дажеwrite(1,"Hello World\n",i++>13||fork()||main())
primo

9

Рубин, 46 знаков

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

Он синхронизируется из-за того, что программа ожидает окончания потока, прежде чем запустить следующий поток и продолжить со следующим символом.


7

Pythonect (35 символов)

http://www.pythonect.org

"Hello World"|list|_.split()->print

Это самый короткий пока. Поскольку я понятия не имею, что это на самом деле делает, я приму это правильно и приму это через день или два, если никто не высказывается против этого или не публикует что-то более короткое.
Тарвен

1
Просто кратко ознакомился с примерами. Разве в операторе print или list не должно быть скобок []?
Далин Сейврайт

1
Привет, я пересылаю ответ Ицик (создатель pythonect): '->' и '|' оба оператора Pythonect. Оператор трубы передает один элемент за элемент, в то время как другой оператор передает все элементы одновременно. Что делает программа выше, так это то, что она берет строку «Hello World», превращает ее в список, разбивает список на символы и отправляет каждый символ на печать. Можно оптимизировать программу еще дальше, к следующему: iter ("Hello World") | print Что он делает, так это перебирает строку «Hello World» и отправляет каждый символ для печати (в режиме синхронизации / блокировки). С уважением, Ицик Котлер | ikotler.org
Леон Федотов

как продвигается нить здесь ???
Рохит

1
Как упомянуто @LeonFedotov и из источника pythonect (доступного в pythonect ) после синтаксического анализа, для каждой итерации и оператора '->' создание потоков выполняется следующим образом: thread = threading.Thread (target = __ run, args = ([( оператор, элемент)] + выражение [1:], copy.copy (globals_), copy.copy (locals_), return_value_queue, не iterate_literal_arrays)) thread.start ()
Джонатан Ром

6

Питон ( 101 93 98)

Это решение Питера Тейлора. Это работает, задерживая печать N-го символа на N секунд. Смотрите комментарии.

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

Это оригинал:

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

Это сработало, потому что время, которое требуется для печати одного символа, меньше, чем время, которое требуется Python для инициализации нового потока, поэтому N-й поток завершит работу до того, как будет создан N + 1-й поток. Очевидно, что на это нельзя полагаться.


Вы можете сохранить 3 символа, изменив import sys,threadingна, import sys,threading as tи вы можете сохранить еще 2, передав аргументы Thread как позиционные аргументы, а не как аргументы с ключевыми словами.
Джоэл Корнетт

2
Где находится код, который касается безопасности потоков? Вы просто запускаете потоки, надеясь, что они будут работать в том же порядке, в котором вы их запускаете. Это не всегда будет правдой и на самом деле является "трудной частью" этой проблемы. Вместо того, чтобы оптимизировать размер вашей программы, вы должны пересмотреть проблему: вы не поняли ее изначально. См. Gist.github.com/2761278 для доказательства того, что этот код не работает.
Сильвиот

Быстрая починка. Используйте threading.Timerвместо threading.Thread. Передайте xкак параметр сна.
Джоэл Корнетт

1
Предложение Джоэла может быть улучшено 4 кfor x in range(11):t.Timer(x,sys.stdout.write,"Hello World"[x]).start()
Питер Тейлор

1
@silviot: я использовал тот факт, что создание потока включает создание экземпляра объекта и, таким образом, занимает от одной до двух третей миллисекунды в системах, которые я тестировал. Вывод персонажа не имеет этих накладных расходов, занимая только одну десятую этого времени. Следовательно, это будет «всегда» работать, пока вы ничего не переопределите. Stdout буферизуется, так что не должно возникнуть проблем.
марин

4

C # 73

"hello world".ToList().ForEach(c=>Task.Run(()=>Console.Write(c)).Wait());

не уверен, что это удовлетворяет требованию, чтобы каждая буква печаталась через собственный поток, так как tpl может повторно использовать потоки.
statichippo

Теоретически вы правы, но на моем компьютере ThreadPool.GetMaxThreads или ThreadPool.GetAvailableThreads возвращает значение около 1000 как для ввода-вывода, так и для рабочих потоков.
JJoos

4

APL (Dyalog Unicode) , 28 байтов SBCS

Полная программа. Печать в stderr. Вдохновленный решением Маринус .

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

Попробуйте онлайн!

⍳11 первые 11 целых

'Hello World'{}&¨ Для каждого целого числа в качестве правого аргумента ( ), создайте следующую функцию с соответствующим символом в качестве левого аргумента ( ):

⎕DL⍵д е л ау правой аргумент секунды

⍺⊣ отменить это (эффективная задержка) в пользу символа левого аргумента

⍞← выведите это на стандартный вывод без завершающего переноса строки


как насчет ⍞∘←&¨'dlroW olleH'? - Я не знаю, гарантировано ли это теоретически, но, кажется, он всегда печатает их в правильном порядке
нгн

@ngn Очевидно, поскольку нет гарантии, что потоки будут работать в том порядке, в котором вы их запускаете, вы должны обеспечить безопасность потока вашей программы, чтобы вывод печатался в правильном порядке.
Адам

это ограничение, которое я пытался устранить, это может быть гарантировано. Я запустил это 100 раз, и похоже, что планировщик потоков всегда выбирает потоки в обратном порядке. Или, по крайней мере, это тот случай, когда есть ≤11 задач. AFAIK ⍞∘←не прерывается (или это? Может быть, вы можете спросить разработчика C?). Dyalog реализует зеленые потоки - 1 реальный поток претендует на множество, поэтому, если (зеленый) переключение потоков не может произойти, порядок будет предсказуемым.
нгн

3

Ява (160 символов)

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();}}

Да, я знаю, что это неправильный язык для гольф-кода, я делаю это для удовольствия.


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 символов
Принц Джон Уэсли

@ Принц Да, спасибо за исправление!
Малкольм

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
символа

@ Ваутер Очень хороший! Я полностью пропустил это.
Малкольм

1
@ Малкольм, @ 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 символов
Принц Джон Уэсли

2

Баш (64)

:(){ [ "$1" ]&&(echo -n "${1:0:1}"&: "${1:1}")};: "Hello World"

@marinus Игра в гольф на 3 символа::()([ "$1" ]&&(printf "${1:0:1}"&: "${1:1}"));: Hello\ World
Цифровая травма

@fossilet Работает для меня как в Linux, так и в OSX, в нескольких версиях bash. Последние один или два символа иногда печатаются после приглашения оболочки.
Цифровая травма

2

Хаскелл ( 120 118)

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

Я не совсем уверен в умножении на 9999 - у меня есть 2 ГГц Xeon, на котором он будет работать нормально, даже если вы этого не делаете, но у меня также есть Pentium 4, который нуждается в этом (999 дал искаженный вывод и 99 не сделал ') вообще ничего не делать.)


Сохранить 2 символа, используя (*5^6)вместо (*9999)и не используя обратные кавычки для mapM_.
Механическая улитка

@Mechanicalsnail Если вы удаляете галочки, вам нужна дополнительная пара фигурных скобок, в противном случае она разбирает то, (((mapM_ (\(x,y) ... )) zip) [0..]) ...что вам не нужно.
Маринус

Что касается 999, это может быть обрезано до 0 из-за ограничений операционной системы, но я могу ошибаться. Какую ОС вы используете?
Джои Адамс



1

D (135 символов)

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..$]);
}

Я начинаю следующий поток, только когда уже напечатал текущий символ

отредактируйте +2 символа для лучшей проверки


Я получаю core.exception.RangeError@test.d(6): Range violationошибку.
Fish Monitor

@fossilet Я исправил это
урод

1

Скала 74

"Hello World".zipWithIndex.par.map(x=>{Thread.sleep(x._2*99);print(x._1)})
  • zipWithIndex производит ((H, 0), (e, 1), (l, 2) ...).
  • Par делает это параллельной коллекцией.

тесты:

(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

scala> "Hello World".zipWithIndex.par.foreach(x=>{Thread.sleep(x._2*99);print(x._1)}) Hel lWrolod- Я получил это
принц Джон Уэсли

Также println(Thread.currentThread.getName)показывает, что темы не являются уникальными.
Принц Джон Уэсли

@PrinceJohnWesley: Полагаю, вам нужно одно ядро ​​на букву, чтобы par распределял работу по всем ядрам.
пользователь неизвестен

Я понял. поэтому требуется одно ядро ​​на букву + высокое разрешение системных часов.
Принц Джон Уэсли

использовать mapвместо foreach. Вы можете сохранить 4 символа.
Принц Джон Уэсли



1

Это моя попытка F #. Моя первая серьезная F # программа. Пожалуйста, будь добр.

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


0

Эрланг (90)

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

Compile erlc +export_all h.erl



0

Python: слишком много символов, но это работает.

# 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()

Я не понимаю, какова цель рандомизации времени сна.
Джоэл Корнетт

@Joel, чтобы убедиться, что когда это работает, это не случайное совпадение потоков, выполняемых в том же порядке, в котором они были запущены.
Сильвиот

0

C # 90 84

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

Рабочая версия: http://ideone.com/0dXNw


console.write очень быстрый, поэтому это может сработать для вас, но определенно не является поточно-ориентированным!
statichippo

@statichippo ты прав; Я исправил это.
Кристиан Лупаску

0

Objective-C (183 символа)

-(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]);}}

0

Haskell 99 персонажей

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

Как это работает, каждый поток начинает следующий после отображения своего символа, поэтому они действительно полезные вещи не могут произойти из последовательности.


0

Баш , 43 байта

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

Попробуйте онлайн!

xargsразветвляется отдельный printfпроцесс для каждого персонажа (и ожидает его выхода).

Bash , 45 байт, никаких внешних утилит

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

Попробуйте онлайн!

Расширяется (printf H); (printf e); (printf l); (printf l); (printf o); (printf \ ); (printf W); (printf o); (printf r); (printf l); (printf d);до оценки. Скобки делают Bash форком для каждой буквы (и ждут его выхода), но на этот раз printfвстроена Bash.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.