出力の一時停止と再開
Posted feedbacks - Nested
Flatten Hiddenこんにちは。 このコードは環境に依存しています。標準ではありません。 でも、C/C++だとstdioもiosteamもブロッキングされちゃうのでこういうコード書くしか手がないと思われます。 抜け道募集。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #include <stdio.h>
#include <conio.h>
int main(){
bool Output=true;
int ch=0;
do{
ch = 0;
if(_kbhit()) ch = _getch();//環境依存コード。
if(ch == 'p') Output = !Output;
if(Output) printf("A");
}while(ch != 'q');
}
|
うーん。こんにちは。 えらそうなこと言っておきながら仕様満たしてなかったですね。 1秒毎に表示するようにしました。 ほかの方などがいわれてるの見て気づいたんですけど、あるぅぇえええ??って感じです。(汗
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | #include <stdio.h>
#include <time.h>
#include <conio.h>
int main(){
bool IsOutput=true;
int ch=0;
int TimeCount=0,TimeOld=0;
do{
ch = 0;
if(_kbhit()) ch = _getch();//環境依存コード。
if(ch == 'p') IsOutput = !IsOutput;
if(IsOutput){
TimeCount = time(NULL);
if(TimeCount != TimeOld){
printf("A");
TimeOld = TimeCount;
}
}
}while(ch != 'q');
return 0;
}
|
ども、raynstardです。 入力がブロッキングされてしまうのは 入力モードを変更してあげないからですね。 UNIXでgetche()の作り方というのを調べるといろいろと出てくると思います。 ポイントはtermiosでカノニカルモードを解除してあげていることと 標準出力のバッファリングをなしにしているところでしょうか? 標準入力の方はおまけです。 // gcc -Wall -std=c99 doukaku179.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | #include <stdio.h>
#include <stdbool.h>
#include <termios.h>
#include <time.h>
static struct termios termios_save;
static bool isContinued = true;
static bool enableOutputScreen = true;
int initKeyInput(void)
{
struct termios t;
tcgetattr(0, &termios_save);
tcgetattr(0, &t);
t.c_lflag &= ~(ICANON|ECHO);
t.c_cc[VMIN] = 0;
t.c_cc[VTIME] = 0;
tcsetattr(0, TCSANOW, &t);
setvbuf(stdin, NULL, _IONBF, 0);
return 0;
}
int main(int argc, char *argv[])
{
char key;
ssize_t read_size;
time_t old, now;
initKeyInput();
setvbuf(stdout, NULL, _IONBF, 0);
old = 0;
while( isContinued )
{
now = time(NULL);
if( now != old )
{
old = now;
if( enableOutputScreen == true )
{
printf("a");
}
}
read_size = fread(&key, 1, sizeof(key), stdin);
if( read_size > 0 )
{
switch( key )
{
case 'q': /* quit */
isContinued = false;
break;
case 'p': /* pause */
enableOutputScreen = (enableOutputScreen != true);
break;
default:
break;
}
}
}
tcsetattr(0, TCSANOW, &termios_save);
printf("\n");
return 0;
}
/* EOF */
|
Javaでは、STDINがバッファされてしまうため Enterが押下されるまで読み込めません。 そのため、下記のコードでは p + Enterと、q + EnterでSuspendとQuitします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | public class Sample179 implements Runnable {
private final Object lockObj_ = new Object();
private boolean isSuspend_ = false;
public boolean getSuspend() {
synchronized (lockObj_) {
return isSuspend_;
}
}
public void changeSusupend() {
synchronized (lockObj_) {
isSuspend_ = !isSuspend_;
if (isSuspend_) {
lockObj_.notifyAll();
}
}
}
@Override
public void run() {
try {
while (Thread.currentThread().isAlive()) {
synchronized (lockObj_) {
while (getSuspend()) {
lockObj_.wait(1000);
}
}
System.out.print('a');
Thread.sleep(1000);
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
public static void main(String[] args) {
final Sample179 sample = new Sample179();
Thread thread = new Thread(sample);
thread.setDaemon(true);
thread.start();
try {
while (true) {
char c = (char) System.in.read();
if (c == 'q') break;
if (c == 'p') {
sample.changeSusupend();
}
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
|
Squeak Smalltalk で。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | | process |
process := [
World findATranscript: nil.
[ Transcript show: $a.
(Delay forSeconds: 1) wait
] repeat
] fork.
[ ActiveHand checkForMoreKeyboard ifNotNilDo: [:event |
event keyCharacter = $q ifTrue: [^process terminate].
event keyCharacter = $p ifTrue: [
process isSuspended
ifTrue: [process resume]
ifFalse: [process suspend]]].
Processor yield.
] repeat
|
適当に拡張性を考えて書いてみた。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | #include <stdio.h>
#include <conio.h>
#include <time.h>
#define STATE_RUN 0
#define STATE_WAIT 1
#define STATE_QUIT 2
int main(){
time_t now,last;
int c;
int state=STATE_RUN;
last=time(NULL);
do{
if(kbhit()){
c=getch();
switch(c){
case 'q':
state=STATE_QUIT;
break;
case 'p':
if(state==STATE_RUN){
state=STATE_WAIT;
}else{
state=STATE_RUN;
}
}
}
switch(state){
case STATE_RUN:
now=time(NULL);
if(last-now!=0){
last=now;
printf("a");
fflush(stdout);
}
}
}while(state!=STATE_QUIT);
return 0;
}
|
fork ってみた
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | use strict;
use warnings;
use POSIX qw( SIGKILL SIGINT );
BEGIN { system 'stty -echo -icanon >/dev/tty' }
END{ system 'stty echo icanon >/dev/tty' }
my $pid = fork;
if($pid) {
for(;;){
my $a = getc;
if($a eq 'q') {
kill SIGKILL , $pid;
exit;
}
elsif($a eq 'p') {
kill SIGINT , $pid;
}
}
}
else {
my $flag = 1;
local $| = 1;
local $SIG{INT} = sub(){ $flag = !$flag };
for(;;){
print "a" if $flag;
sleep 1;
}
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | use strict;
use threads;
use threads::shared;
use Term::ReadKey;
$| = 1;
my $f : shared;
sub observe {
ReadMode 'cbreak';
while(1){
my $key;
while(1){ last if $key = ReadKey(0) }
{
lock $f;
if ($key eq 'q'){ ReadMode 'restore'; exit;}
if ($key eq 'p'){ $f == 1 ? $f-- : $f++;}
}
}
}
sub print_a {
while(1){
if($f == 1){
print "a";
sleep 1;
}
}
}
$f = 1;
my $ob = threads->create(\&observe);
my $pr = threads->create(\&print_a);
$ob->join;
$pr->join;
|
標準出力といえるかかなり微妙ですが、Processing で。ウィンドウ中央に適当な色で a を書き続けます。
see: Processing
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | boolean pause = false;
void setup(){
size(200,200);
frameRate(1);
// "Processing -> Tools -> Create Font ..." and save "Courier-48.vlw"
PFont font = loadFont("Courier-48.vlw");
textFont(font, 48);
textAlign(CENTER);
}
void draw() {
if (! pause) {
fill(random(255),random(255),random(255));
text("a", width/2, height/2);
}
}
void keyPressed() {
switch (key) {
case 'p':
pause = !pause;
break;
case 'q':
fill(0, 30);
text("quit", width/2, height/2);
exit();
break;
}
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | #load "unix.cma";;
open Unix;;
(*エコー、行バッファoff*)
let x = tcgetattr Unix.stdout in
tcsetattr Unix.stdout TCSADRAIN {x with
c_icanon = false; c_vmin = 1; c_echo = false; };
at_exit (fun () -> tcsetattr Unix.stdout TCSADRAIN x;);
(*一秒ごとのa*)
Sys.set_signal Sys.sigalrm (Sys.Signal_handle
(fun _ -> print_char 'a'; flush Pervasives.stdout));
(*本編*)
let t = ref (setitimer ITIMER_REAL {it_interval=1.0; it_value=1.0}) in
while true do
match input_char Pervasives.stdin with
| 'q' -> exit 0
| 'p' -> t := setitimer ITIMER_REAL !t
| _ -> ()
done;;
|
ざっくりと。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | module Doukaku179 = begin
type PeriodicAutoWriter = class
val mutable timer : System.Threading.Timer
new() = { timer = null }
member t.Start() = t.timer <- new System.Threading.Timer(
(fun _ -> printf "%s" "a"), null, 0, 1000)
member t.Stop() = if t.timer <> null then (t.timer.Dispose();
t.timer <- null)
else ()
member t.Toggle() = if t.timer = null then t.Start() else t.Stop()
end
let read_char () = System.Console.ReadKey().KeyChar
let keep_read () = seq { while true do yield read_char () done }
let run () = let writer = new PeriodicAutoWriter() in
writer.Start ();
seq { for c in keep_read () do
yield (match c with
'q' -> writer.Stop(); false
| 'p' -> writer.Toggle(); true
| _ -> true)
done }
|> Seq.find ((=) false)
|> (fun _ -> printfn "%s" "");;
end;;
#if COMPILED
let _ = Doukaku179.run ();;
#endif
|
man bashしたらいけることがわかったので作ってみました
1 2 3 4 5 6 7 | stty -echo
while echo -n a && read -t 1 -n 1 a || [ "$a" != q ]; do
if [ "$a" = p ]; then
while read -n 1 a && [ "$a" != p ]; do :; done
a=
fi
done
|
System.Timers.Timerクラスを使用しています。Timerクラスは指定した間隔でイベントを発生させます。
後はループを回して、その中でTimerの停止、再開を切り替えています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | using System;
using System.Timers;
class Program {
static void Main(string[] args) {
using(Timer timer = new Timer(1000)) {
timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
timer.Start();
bool flag = true;
while(flag) {
ConsoleKeyInfo conKeyInfo = Console.ReadKey(true);
ConsoleKey conKey = conKeyInfo.Key;
switch(conKey) {
case ConsoleKey.P:
timer.Enabled = !timer.Enabled;
break;
case ConsoleKey.Q:
flag = false;
break;
}
}
timer.Close();
}
}
static void timer_Elapsed(object sender, ElapsedEventArgs e) {
Console.Out.WriteLine("a");
}
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | require 'Win32API'
GetKeyState = Win32API.new('user32', 'GetKeyState', 'I', 'I')
pause = false
Thread.start {
loop {
print 'a' unless pause
sleep 1
}
}
loop {
key_state = {:p => (GetKeyState.call(?P)[7] == 1),
:q => (GetKeyState.call(?Q)[7] == 1)
}
exit if key_state[:q]
if key_state[:p]
pause = !pause
sleep 0.3
else
sleep 0.1
end
}
|
pキーの長押し時、動作に不具合があったので修正しました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | require 'Win32API'
GetKeyState = Win32API.new('user32', 'GetKeyState', 'I', 'I')
pause = false
can_toggle_pause = true
Thread.start {
loop {
unless pause
print 'a'
sleep 1
else
sleep 0.1
end
}
}
loop {
key_state = {:p => (GetKeyState.call(?P)[7] == 1),
:q => (GetKeyState.call(?Q)[7] == 1)
}
exit if key_state[:q]
if key_state[:p]
if can_toggle_pause
pause = !pause
can_toggle_pause = false
end
else
can_toggle_pause = true
end
sleep 0.05
}
|
Timerクラス使うのと大差ないコード量ですね、若干挙動は違いますが。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | using System;
using System.Threading;
class Program {
static void Main(string[] args) {
Thread thread = new Thread(new System.Threading.ThreadStart(tick));
thread.IsBackground = true;
thread.Start();
bool flag = true;
while(flag) {
switch(Console.ReadKey(true).Key) {
case ConsoleKey.P:
switch_ = !switch_;
break;
case ConsoleKey.Q:
flag = false;
break;
}
}
}
static bool switch_ = true;
static void tick() {
while(true) {
if(switch_) {
Console.Out.Write("a");
}
Thread.Sleep(1000);
}
}
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | import curses
w = curses.initscr()
try:
curses.noecho()
w.timeout(1000)
flg = True
while True:
c = w.getch()
if c == -1 and flg:
w.addch('a')
elif c == ord('q'):
break
elif c == ord('p'):
flg = not flg
except:
pass
curses.endwin()
|
sbcl依存のthreadパッケージを利用しています。一つわからない ことがあって、それはdefpackage - in-packageの流れでパッケー ジ化すると、うまく動かないところです。 thread programmingははじめてなので、変数の取り扱いの 注意点が抜けてるみたいです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | (defun print-a ()
(format *standard-output* "a")
(finish-output)
(sleep 1))
(defmacro read-thread(res)
`(sb-thread:make-thread
(lambda()(setf ,res (read *standard-input*)))))
(defun main ()
(let* ((res nil)
(stop nil)
(thread (read-thread res)))
(loop do
(if (null (sb-thread:thread-alive-p thread))
(case res
((q)
(return))
((p)
(and stop (print-a))
(setf stop (not stop)
thread (read-thread res)))
(otherwise
(unless stop (print-a))
(setf thread (read-thread res))))
(unless stop (print-a))))))
|
threadを扱うときに変数の入り方がわかりにくかったですね。 q<return>すると resの中には'common-lisp-user::q と入力さ れていました。こんなことあるんですね。
1 2 3 4 5 6 7 8 9 10 11 | 0a1,4
> (defpackage :dokaku179
> (:use :common-lisp :sb-thread))
> (in-package :dokaku179)
>
8c12,14
< (lambda()(setf ,res (read *standard-input*)))))
---
> (lambda()(progn
> (in-package :dokaku179)
> (setf ,res (read *standard-input*))))))
|
Haskellでも多言語同様にコンソール入力がエンターを押すまで読めない病にかかっています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | module Main where
import System
import System.IO
import System.IO.Error
import Control.Monad
weaveIO :: (IO Bool) -> IO a -> (a -> IO a) -> IO a
weaveIO w m f = do
a <- m
b <- w
if b then f a
else return a
getChar' :: IO Char
getChar' = getLine >>= return.head.(++" ")
nonBlockingGetCh :: IO (Maybe Char)
nonBlockingGetCh = do
f <- hReady stdin
if (not f) then return Nothing
else do
getChar' >>= return.Just
respondToKey :: IO Bool
respondToKey = nonBlockingGetCh >>= evalCh
where
evalCh Nothing = return True
evalCh (Just 'q') = print "+++ quitting +++" >> return False
evalCh (Just 'p') = waitForP ' '
evalCh _ = return True
waitForP 'p' = return True
waitForP 'q' = print "+++ quitting +++" >> return False
waitForP _ = print "+++ paused, hit 'p' again +++" >> getChar' >>= waitForP
showLineWeave :: Handle -> IO ()
showLineWeave h = weaveIO (respondToKey) (hGetLine h >>= putStrLn) (\_ -> showLineWeave h)
main = do
args <- getArgs
withFile (head args) ReadMode $ \h ->
catch (showLineWeave h) (\e -> if (isEOFError e) then return () else ioError e)
|
あー、匿名で投稿してしもた...
これ、題意からそれてますねぇ…一秒後との出力じゃないし…
こちらが題意に沿ったものになります
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | module Main where
import System
import System.IO
import System.IO.Error
import Control.Monad
import Control.Concurrent
weaveIO :: (IO Bool) -> IO a -> (a -> IO a) -> IO a
weaveIO w m f = do
a <- m
b <- w
if b then f a
else return a
getChar' :: IO Char
getChar' = getLine >>= return.head.(++" ")
nonBlockingGetCh :: IO (Maybe Char)
nonBlockingGetCh = do
f <- hReady stdin
if (not f) then return Nothing
else do
getChar' >>= return.Just
respondToKey :: IO Bool
respondToKey = nonBlockingGetCh >>= evalCh
where
evalCh Nothing = return True
evalCh (Just 'q') = print "+++ quitting +++" >> return False
evalCh (Just 'p') = waitForP ' '
evalCh _ = return True
waitForP 'p' = return True
waitForP 'q' = print "+++ quitting +++" >> return False
waitForP _ = print "+++ paused, hit 'p' again +++" >> getChar' >>= waitForP
showLineWeave :: IO ()
showLineWeave = weaveIO (respondToKey) (putStr "a" >> hFlush stdout >> threadDelay 1000000) (\_ -> showLineWeave)
main = showLineWeave
|
スレッド? そりゃ食いもんか?
BASIC の INKEY$ はブロッキングしないので、ゲームとか作るのに便利です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 100 *Init '*** 初期化 ***
110 cls
120 t$=time$
130 active=1 ' タイマー実行中フラグ
140 *MainLoop '*** メインループ ***
150 ' キー入力を1文字読み込む
160 c$=inkey$
170 ' qで終了、pで停止/再開
180 if c$="q" then end
190 if c$="p" then active=1-active
200 ' time$ (=hh:mm:ss) が変化したらタイマー発動
210 if t$=time$ then goto *MainLoop
220 t$=time$
230 if active=1 then gosub *OnTimer
240 goto *MainLoop
250 *OnTimer '*** タイマーイベントハンドラ ***
260 print "a"
270 return
|
無謀にも PostScript で。 GhostScript 7.07 のファイル関連のオペレーターのbug だか仕様だかに泣かされました...
どうにもならなかったので ghostscript 2本並列実行で文字入力と表示を役割分担しています。 プロセス間通信は名前付きファイルで... 激しくファイルの open/close をしますので実用的ではありません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | %% Terminal 1
% stty raw < /dev/tty
% gs -sDEVICE=nullpage echoback.ps
%% Terminal 2
% gs -sDEVICE=nullpage pause.ps
%------------------ echoback.ps ----- Cut Here ---------
%!PS
/Stdin (%stdin) (r) file def
(/tmp/test) (w) file closefile
% p: 112, q: 113
{
Stdin read
{
(/tmp/test) (w) file dup 2 index write closefile
113 eq { exit } if
} {exit} ifelse
} loop
quit
%-------------------- pause.ps ------ Cut Here ---------
%!PS
/Sleep { % OutputFlag Timer Sleep OutputFlag'
realtime
{
realtime 1 index sub
2 index ge {exit} if
(/tmp/test) (r) file dup read
{
exch closefile (/tmp/test) (w) file exch
dup 113 eq { quit } if
112 eq { 4 -1 roll not 4 1 roll } if
} if
closefile
} loop
pop
pop
} def
true
{
1000 Sleep
dup {(a) print flush} if
} loop
|
see: jQuery
1 2 3 4 5 6 7 8 9 10 11 | <script src="jquery-1.2.6.js"></script><script>
$(function(){
var stdout = $(document.body);
var pause = false
var loop = setInterval(function(){ pause || stdout.append('a') }, 1000);
$().bind('keydown', function(e){ e = String.fromCharCode(e.keyCode);
'P' == e ? pause = !pause :
'Q' == e ? clearInterval(loop) : 0;
});
})
</script>
|
どう見てもCです。本当にありがとうございました。
標準ライブラリphobosの整備を急いでほしいです
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | private import std.stdio, std.c.time;
void main() {
bool running = true;
for(;;) {
sleep(1);
if(kbhit) {
switch(getch) {
case 'p':
running = !running;
break;
case 'q':
return;
default:
}
}
if(running) {
write('a');
fflush(stdout);
}
}
}
|





nobsun
#6346()
Rating3/3=1.00
起動すると、標準出力に1秒毎に'a'の1文字を出力し続けるプログラムで、 以下の条件を満たすものを「どう書く?」
[ reply ]