LINUX.ORG.RU
ФорумTalks

Обратный отсчёт

 ,


0

2

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

Пример на bash:

#!/bin/bash

let SEC=$[ $1 * 60 ]

echo seconds left:

while( [ $SEC -gt 0 ] )
do
echo $SEC 
let SEC--
sleep 1
done

Результат прогона:

./countdown.sh 3
seconds left:
180
179
178
177
176
175
^C

Перемещено Zhbert из development


Development который мы заслужили.

lua
()

while( [ $SEC -gt 0 ] )
do
echo $SEC
let SEC--
sleep 1
done

Это неправильная программа. При 3 мин = 180 сек она завершится примерно секунд через 183 или больше.

Kroz ★★★★★
()

Как уже верно заметили выше, такую задачу нельзя решить при помощи sleep по очевидным причинам.

Octagon
()
Ответ на: комментарий от Black_Shadow

C

А затем, чтобы перерывы делать: для себя любимого по СанПИНу на 15 минут каждый час, чтобы от монитора не ослепнуть; или по-договорённости - на время стримов чайку попить на кухне… да много вариантов.

Пример на классическом Си (надо бы обработку ошибок улучшить/доделать):

#include <stdio.h>
#include <unistd.h>

int main(int argc, char * argv[]){
	if(argc<2)
		return 1;

	int sec;

	sscanf(argv[1], "%d", &sec);
	sec *= 60;
	puts("Seconds left:\n");
	for( ;sec; sec--) {
		printf("%d\n", sec);
		sleep(1);
	}

	return 0;
}

А что на Lua, кроме неинформативных и неинтересных комментариев ничего больше не предвидится?

Android
() автор топика
Ответ на: комментарий от seiken

Автор не указал, какая необходима точность. М.б. +3 секунды на 3 минуты для него ОК.

Конечно. +-3 секунды и фотографии при проявлении на бумаге или засветятся или затемнятся… :-)

Самим не смешно?

Примерно время засечь (см. выше).

Android
() автор топика
Ответ на: комментарий от Android

и фотографии при проявлении на бумаге

Ничего нет в задании ни про проявление, ни про вообще какое-либо конкретное применение.

seiken ★★★★★
()

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

i-rinat ★★★★★
()
Ответ на: C от Android
while(sec) printf("%d\n", sec--), sleep(1);

же

olelookoe ★★★
()
Ответ на: комментарий от i-rinat

Ринат, давай за чашечкой кофе с ликером обсудим это! С надеждой на скорую встречу,

Владимир

anonymous
()

Для нелюбителей «однострочников» и любителей асинхронщины, неотстающий таймер:

import asyncio
import datetime
import sys


async def print_task(remaining):
    start_time = datetime.datetime.now()
    while True:
        now = datetime.datetime.now()
        print(remaining - (now - start_time).seconds)
        await asyncio.sleep(1.0)


async def main(remaining):
    await asyncio.wait_for(print_task(remaining), timeout=remaining)


asyncio.run(main(int(sys.argv[1]) * 60))

Или, если неохота исключения ловить, то:

async def main(remaining):
    task = asyncio.create_task(print_task(remaining))
    await asyncio.sleep(remaining)
    task.cancel()
vvn_black ★★★★★
()
Ответ на: комментарий от vvn_black

Ты очень умный мужчинка! Не то что мой дурачок Руслик :(

Владимир

anonymous
()
Ответ на: комментарий от vvn_black

Упс, жара повлияла, всё ещё проще:

import asyncio
import datetime
import sys

async def timer(timeout):
    start_time = datetime.datetime.now()
    countdown = timeout
    while 1 < countdown <= timeout:
        now = datetime.datetime.now()
        countdown = timeout - (now - start_time).seconds
        print(countdown)
        await asyncio.sleep(1.0)


asyncio.run(timer(int(sys.argv[1]) * 60))
vvn_black ★★★★★
()

Подходы со sleep-ами в реальности добавляют к количеству секунд ещё и время выполнения кода между ними. Для очень большого числа это даст ощутимую погрешность. По идее, нужно выносить в разные потоки

package main

import (
	"fmt"
	"log"
	"os"
	"strconv"
	"time"
)

func init() {
	log.SetFlags(0)
}

func argErr(msg string) {
	log.Fatalf(`%s
Use %s [number of seconds to countdown]
`, msg, os.Args[0])
}

func checkArgs() {
	switch {
	case len(os.Args) < 2:
		argErr("Missing number of seconds to countdown")
	case len(os.Args) == 2:
	default:
		argErr("To many arguments")
	}
}

func main() {

	checkArgs()

	var (
		left, err = strconv.ParseInt(os.Args[1], 0, 64)
		tk        *time.Ticker
	)

	if err != nil {
		log.Fatal("invalid number of seconds passed: ", err)
	}

	if left < 0 {
		log.Fatal("zero or negative number of seconds passed: ", left)
	}
	defer fmt.Println("done")

	if left == 0 {
		return //
	}

	tk = time.NewTicker(time.Second)
	defer tk.Stop()

	for range tk.C {
		if left--; left <= 0 {
			break
		}
		fmt.Println("pew-pew", left)
	}
}
kostyarin_ ★★
()
(0...ARGV[0].to_i).reverse_each do |t|
    puts t+1
    sleep 1
end
kostyarin_ ★★
()
Ответ на: комментарий от kostyarin_

Ах-ха-ха. А-за-за.

Костик, не обращай внимание на дурачка. Это мой Руслик пишет, ревнует меня к тебе (

Владимир

anonymous
()

которая бы принимала на вход количество оставшихся до события минут и печатала каждую секунду

на вашем любимом всеми языке

Не знаю зачем кому-то нужно печатать оставшиеся секунды, только если конструкторам ядерной бомбы. Posh:

param ([int] $min)

($min * 60)..0 | % { $_; Start-Sleep -sec 1}
anonymous
()
Ответ на: C от Android

А затем, чтобы перерывы делать: для себя любимого по СанПИНу на 15 минут каждый час

Напиши systemd сервис

#break_message.timer
[Unit]
Description=Break message timer
[Timer]
OnStartupSec=1h
OnUnitActiveSec=1h
[Install]
WantedBy=timers.target

#break_message.service
[Unit]
Description=Break message service
[Service]
Type=oneshot
ExecStart=/usr/bin/swaynag -m "BREAK !!!!"

Не знаю куда тебе лучше слать сообщение, но в моём случае (sway+swaynag) вылезет жирная красная полоса с инфой, не проигнорить.

ЗЫ: хотя тебе подойдёт вряд ли - нужно ведь перезапускать таймер когда уселся после перекура. Но в случае swaynag решаемо - можно выводить в нём кнопки - нажал и таймер перезапустился (через переактивацию сервиса).

pavlick ★★
()
Последнее исправление: pavlick (всего исправлений: 1)

Захотелось для пробы на c++ набросать. Поточнее всяких sleep(1) будет.

#include <chrono>
#include <thread>
#include <iostream>

int main(int argc, const char *agrv[])
{
	using namespace std;
	using namespace chrono;
	if (argc < 2) {
		cout << "pass time, exit\n";
		return 0;
	}
	auto end_time = system_clock::now() + minutes(stoi(agrv[1]));
	auto cur_time = system_clock::now();
	while (cur_time < end_time) {
		cout << duration_cast<seconds>(end_time-cur_time).count() << endl;
		cur_time += seconds(1);	
		this_thread::sleep_until(cur_time);
	}
	cout << "!!! BANG !!!" << endl;
}

pavlick ★★
()
zzz() {
        printf "Wait "
        for i in $(seq ${1} -1 1); do printf "%3ds\b\b\b\b" $i; sleep 1; done
        printf "done\n"
}
beastie ★★★★★
()
function step() {
  countdownSeconds--;
  if (countdownSeconds > 0) {
    console.log("%d с", countdownSeconds);
  } else {
    console.log("ХДЫЩЩ!");
    clearInterval(intervalHandler);
  }
}

function getSeconds() {
  let seconds = Math.round(process.argv[2] * 60);
  if (seconds <= 0) {
    seconds = undefined;
  }
  return seconds;
}

function die() {
  console.log("Неверное количество минут: %s", process.argv[2]);
  console.log("Использование: node %s <количество минут>", process.argv[1]);
  process.exit(1);
}

let countdownSeconds = getSeconds() || die();
const intervalHandler = setInterval(step, 1000);
console.log("Обратный отсчет %d c, осталось:", countdownSeconds);
Nervous ★★★★★
()
Ответ на: C от Android

А затем, чтобы перерывы делать

Пейте больше жидкости. Будете чаще ходить в туалет, заодно и от монитора оторвётесь.

P.S. Молодёжь пошла, без компа уже вообще ничего не может.

ugoday ★★★★★
()

а в цикле считать разницу в секунду по unixtime например не точнее слипа будет?

Anoxemian ★★★★★
()
Ответ на: C от Android

Приобрети никотиновую зависимость (или найди друзей с ней с которыми выходить будешь «курить») и организм сам подскажет.

gnunixon ★★★
()
Ответ на: комментарий от pavlick

SystemD

На SystemD - оригинально , как и сказать at - куда то пробросить результат. Но хочу сказать спасибо за более точные методы со спящими потоками.

А люди с Ассемблерами не подтянутся? Или это чрезчур громоздко будет?

Android
() автор топика
Ответ на: комментарий от n_play

За счёт асинхронности.

Печать оставшихся секунд идёт в отдельной корутине, не зависит от и не влияет на завершение основной.

vvn_black ★★★★★
()
Ответ на: комментарий от vvn_black

асинхронность же просто дает возможность дать поработать другим «корутинам», пока эта одна ждёт сигнала изменения статуса от заказанной io.

поэтому вместо блокирующего sleep применяется асинхронный.

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

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

n_play
()
Ответ на: комментарий от n_play

откуда тогда берется неотстающесть?

Магия!

await asyncio.wait_for(print_task(remaining), timeout=remaining)

Практическое время через которое возникнет исключение и величина отставания от заданного не зависит ни от remaining, ни от того, что делает print_task - просто печатает счётчик, отправляет что-то в БД или что-то считает. Оно больше только на время завершения print_task, т.е. доли секунды.

vvn_black ★★★★★
()

От анонимуса, который мог это написать в Development, но не может в Talks:

Я по теме «Обратного отсчёта». В Юниксах есть системный вызов setitimer (man 2 setitimer), который устанавливает таймер на какое-то время, а после срабатывания таймера обновляет его значение и таким образом получается таймер, срабатывающий с некоторой периодичностью и не отстающий ни на долю секунды. Во время «срабатывания» таймера генерируется сигнал SIGALRM.

В питоне есть биндинг в стандартной библиотеке: https://docs.python.org/3/library/signal.html#signal.setitimer

Насколько я знаю, sleep() в Линуксе реализован тоже через SIGALRM, поэтому его не получится заюзать вместе с таймером. Но заблокировать основную ветку программы можно и другими способами.

Просто захотелось поделиться, но, к сожалению, в толксы анон не может.

Извините за офтоп.

vvn_black ★★★★★
()
Последнее исправление: vvn_black (всего исправлений: 1)
Ответ на: комментарий от vvn_black

всё ясно, проглядел самую мякотку.

wait_for
timeout=

т.е. основное дело делает оно.

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

n_play
()

А у тебя ядро реального времени?

BceM_IIpuBeT ★★☆☆☆
()
Ответ на: комментарий от vvn_black

TMTOWTDI (And Mine Will Use As Few Letters As Possible Just Because That’s What How The Koo! K1dz Roll)

t184256 ★★★★★
()
Ответ на: комментарий от grim

Еще как отстающий. Если твоему процессу какое-то время не давать выполняться - засуспендить его, например, то он будет показывать неправильный отсчет. И второе, в js нет гарантии что таймер сработает именно через указанный квант. Он может сработать позднее, но не раньше.

mimico
()
Ответ на: комментарий от mimico

Еще как отстающий.

4.2

Если твоему процессу какое-то время не давать выполняться - засуспендить его, например, то он будет показывать неправильный отсчет.

ЛОЛКС

А если его прибить то он совсем отстанет.

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

Ни в одном ЯП насколько я знаю нет.

У каждого запуска будет небольшая дельта, но отставать он не будет

grim ★★☆☆
()
25 августа 2020 г.

Извините за некропостинг. Перешёл из этой темы: Таймер на Rust/C/C++

import time

def main
    @print "input minutes: "
    *seconds @int @scan * 60
    *tStart @int @time:time
    for *lastTime @int in seconds;-1;-0
        *n @int @time:time * -1 + tStart seconds
        if != lastTime n
            @print* lastTime "\n"
            lastTime = n

Если ресурсы процессора жалко, то надо в цикл слип внести миллисекунд на 20 (или какая допустима погрешность). Но слипа пока нет. Вообще, это решение повторяет это: Обратный отсчёт (комментарий) . Со sleep_until было бы лучше, конечно. Зато у меня без единой скобки.

Kogrom
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.