HelolW rdlo(线程挑战)


39

我为您带来挑战:

  • 使用任何语言打印“ Hello World”。
  • 每个字符必须从其自己的唯一线程中打印

而已。显然,由于不能保证线程按启动顺序运行,因此必须确保程序线程安全以确保按正确的顺序打印输出。

并且,因为这是代码高尔夫,所以最短的程序将获胜。

更新:

获奖者是Marinus的APL参赛作品(34个字符)。它还赢得了可读性最差的奖项。


10
一个更好的名字应该是HelolW rdlo
Cristian Lupascu 2012年

哈,我喜欢。立即更改它:D
Tharwen 2012年

糟糕...太短了
Tharwen

1
有趣的是,有多少人忽略了“显然,因为不能保证线程将按照您启动它们的顺序运行”提示,并认为他们做对了。
乔·埃伯特

实际上,“不能保证线程将按照您启动它们的顺序进行操作”,这实际上是对这样琐碎的程序几乎总是会做的事情。为了避免这种混乱,我将添加一个问题,即每个线程必须 1)等待(较小的)毫秒随机数2)输出其char的字符3)等待另一随机(可能很长)的时间这种方式人们可以判断是否该代码只需运行几次即可。而且join()解决方案的性能会差很多。如果没有随机的等待,一个人可能会因成功运行而误以为他的程序是正确的。
silviot 2012年

Answers:


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

好,这是最短的。恭喜你!
塔文(Tharwen)2012年

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语句更改write(1,"Hello World\n",!!++i)为2个字节。否则很好的解决方案。
2012年

您应该尝试一下,看看会产生什么。
面包店2012年

我的意思是说!!++i
primo 2012年

那似乎是您第一次写的内容,所以我看不到您要纠正的错误。而且我并不是在开玩笑:我说的是老实说,您应该自己尝试一下,看看会发生什么。通过删除添加项,每个线程将只打印字符串的第一个字符。
面包箱

我最初是写的!!i++,但几秒钟后进行了编辑,因为我意识到它会0在第一次迭代时求值。我以为你看过未编辑的版本。我无法测试您的代码,因为它只会输出第一个字符一次。但是,还有很多选择。i++<13,使用!!i或什write(1,"Hello World\n",i++>13||fork()||main())
primo 2012年

9

Ruby,46个字符

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

由于程序在启动下一个线程并继续下一个字符之前等待线程结束,因此它被同步。


7

Pythonect(35个字符)

http://www.pythonect.org

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

这是迄今为止最短的时间。由于我不知道它的实际作用,因此我假设它是正确的,如果没人能反对它或发表更短的内容,那么我会在一两天内接受它。
塔文(Tharwen)2012年

1
只是简单地看了看例子。print语句或list语句不应该在方括号[]周围吗?
大林·塞维赖特2012年

1
嗨,我转发了Itzik(pythonect的创建者)的答案:“->”和“ |” 都是Pythonect运算符。管道操作员一项传递一项,而另一名操作员一次传递所有项。上面的程序所做的是,它使用“ Hello World”字符串,将其变成一个列表,将该列表拆分为char,然后将每个char发送给打印。可以进一步优化程序,使其达到以下效果:iter(“ Hello World”)| print它的作用是,迭代“ Hello World”字符串,并发送每个字符以进行打印(以同步/阻止方式)。此致,Itzik Kotler | ikotler.org
利昂·费多托夫

此处的线程处理方式如何???
罗希特(Rohit)

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()
Jonathan Rom

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个线程之前完成。显然,依靠它是违反规则的。


通过更改import sys,threading为可以节省3个字符import sys,threading as t,通过将参数作为位置args(而不是关键字参数)传递给Thread,可以节省2 个字符。
乔尔·科内特

2
处理线程安全的代码在哪里?您只是触发线程,希望它们以与启动它们相同的顺序运行。这并不总是正确的,实际上是这个问题的“硬部分”。除了优化程序大小之外,您应该重新考虑问题:您一开始就没有意识到。有关此代码无效的证明,请参见gist.github.com/2761278
silviot 2012年

快速解决。使用threading.Timer代替threading.Threadx作为睡眠参数传递。
乔尔·科内特

1
乔尔(Joel)的建议可以提高4至for x in range(11):t.Timer(x,sys.stdout.write,"Hello World"[x]).start()
彼得·泰勒

1
@silviot:我正在利用线程创建涉及实例化对象这一事实,因此在我测试的系统上花费了三分之一到三分之二毫秒的时间。字符输出没有这种开销,仅此时间的十分之一。因此,只要您不覆盖任何内容,它将“始终”起作用。Stdout已缓冲,因此也永远不会出现问题。
marinus 2012年

4

C#73

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

由于tpl可能会重用线程,因此不能确定这是否满足通过其自己的线程打印每个字母的要求。
statichippo

从理论上讲,您是对的,但是在我的PC上,ThreadPool.GetMaxThreads或ThreadPool.GetAvailableThreads会为IO和工作线程返回大约1000的值。
JJoos 2012年

4

APL(Dyalog Unicode),28 字节SBCS

完整程序。打印到stderr。受到marinus解决方案的启发。

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

在线尝试!

⍳11 前11个整数

'Hello World'{}&¨ 对于每个作为右参数()的整数,产生以下函数,并将相应的字符作为左参数():

⎕DL⍵d Ë AY右参数秒

⍺⊣ 抛弃(有效延迟)以支持左参数的字符

⍞← 将其打印到标准输出而不会出现换行符


怎么样⍞∘←&¨'dlroW olleH'?-我不知道理论上是否可以保证,但是似乎总是以正确的顺序打印它们
ngn

@ngn 显然,由于不能保证线程将按照启动它们的顺序运行,因此必须确保程序线程安全以确保以正确的顺序打印输出。
亚当

这就是我要解决的约束,可以保证。我运行了100次,看起来线程调度程序总是以相反的顺序拾取线程。或至少有≤11个任务时就是这种情况。AFAIK ⍞∘←不是可中断的(或者是?也许您可以问问C开发人员?)。Dyalog实现了绿色线程-假装有很多真实线程,因此如果无法发生(绿色)线程切换,则该顺序可预测的。
ngn

3

Java(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个字符
约翰·卫斯理亲王

@叶爷,谢谢你的指正!
马尔科姆2012年

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

@Wouter很好!我完全想念它。
马尔科姆

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个字符
约翰·卫斯理亲王

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版本)。在shell提示符后有时会打印最后一个或两个字符。
Digital Trauma 2014年

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

我不确定要乘以9999-我有一个2Ghz Xeon,即使您不使用它也可以正常工作,但我也有需要它的Pentium 4(999输出乱码,而99则输出乱码)根本不做任何事情。)


通过使用(*5^6)代替(*9999)和不使用反引号保存2个字符mapM_
机械蜗牛

@Mechanicalsnail如果删除反引号,则需要额外的一对花括号,否则它将解析为不需要的花括号(((mapM_ (\(x,y) ... )) zip) [0..]) ...
marinus 2012年

至于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

Java语言(72)

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

1

斯卡拉(45)

基于Thread#join的解决方案

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

要么

for(x<-"Hello World")new Thread{print(x)}join

1

这是我的F#尝试。我的第一个认真的F#程序。请客气。

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

0

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}}

您无需计算字符-我们将为您计算!
用户未知

0

二郎(90)

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

编译 erlc +export_all h.erl


0

宁罗德(121)

proc p(c:char){.thread.}=write(stdout,c)
for c in "Hello World":
 var t:TThread[char]
 createThread(t,p,c)
 joinThread(t)

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确保在工作时以与被激发相同的顺序执行线程并不是幸运的巧合。
silviot 2012年


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

Bash,43个字节

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

在线尝试!

xargsprintf为每个字符分叉一个单独的进程(并等待其退出)。

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内置的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.