Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Perl: Общие вопросы > Календарь произвольных дат


Автор: vumnik 5.6.2006, 13:20
 Здравствуйте. У меня есть интересная, на мой взгляд, задача. Нужно разработать функцию или класс позволяющие вывести произвольный набор дат (в текстовый файлик или на консоль)в строчку, примерно вот так.

          январь             февраль                март      июнь
  Пн       9  16  23  30      6  13  20  27        6  13     5  12
  Вт      10  17  24  31      7  14  21  28       7  14      6  13
  Ср      11  18  25       1   8  15  22        1   8           7  14
  Чт      12  19  26       2   9  16  23        2   9           8  15
  Пт      13  20  27       3  10  17  24       3  10          9  16
  Сб  7  14  21  28       4  11  18  25       4  11        10  17
  Вс   8  15  22  29       5  12  19  26       5  12        11

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

Код

#определение дня недели по дате
sub DayOfWeek {
    ($d, $m, $y) = @_;

    if ($m < 3) {
        $m += 10;
        $y--;
    }
    else { $m -= 2; }

    $cen = div($y,100);
    $yr = $y % 100;

    $dw = (div((26*$m - 2),10) + $d + $yr + div($yr,4) + div($cen,4) - (2*$cen)) % 7;
    if ($dw < 0)        { $DayOfWeek = $dw + 7; }
    else            { $DayOfWeek = $dw;    }
    #буржуйское начало недели меняем на наше
    if ($DayOfWeek == 0)    { $DayOfWeek = 6;    }
    else            { $DayOfWeek -= 1;    }
    
    return $DayOfWeek;
}


 Не нашёл целочисленного деления (div  в паскале). Если кто встречал, ткните носом, ибо слеп. Вот мой вариант.

Код

#Целочисленное деление
sub div {
    ($a, $b)  = @_;

    $c = $a/$b;
    if ($c =~ /\d+(?=\.)/)    { return $&;}
    else            {return $c; }
}


 Осилил функцию, корректно выводящую календарь на один месяц. 

Код

@dow = ('Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Вс');
#создание и отрисовка календаря
sub calendar {
    my ($d1,$m1,$y1,$d2,$m2,$y2) = @_;
    
    #календарь будем хранить в матрице    
    #в первом столбце будем хранить название дней недели
    for ($j=0; $j<7;$j++) {
        $mas[$j][0] = $dow[$j];
    }
    
    #заполняем матрицу календаря
    $dow = DayOfWeek($d1,$m1,$y1);
    $j = $dow;
    for ($i=1;$i<7;$i++) {
        while ($j < 7) {
            if ($d1<=$d2) {
                $mas[$j++][$i] = $d1++ ;
            } else {
                $mas[$j++][$i] = "";
            }
        }
        $j = 0;
    }

    #вывод календаря в файл
    #printf FILE "%13s\n", $ear{$m1-1}[0];
    for ($i=0;$i<7;$i++) {
        for ($j=0;$j<7;$j++) {
            printf FILE "%4s", $mas[$i][$j],"\t";
        }
        print FILE "\n";
    }
}


 Оставшиеся нерешёнными вопросы:
    1. Как анализировать и выводить больше одного интервала дат.
    2. Хранить ли сформированный календарь всех интервалов в общей матрице, а затем выводить его, или как-то можно выводить по отдельности в строку (как в образце календаря).

 Если кому интересно, подскажите, сам я запутался. 

Автор: korob2001 5.6.2006, 15:37
Сейчас уходить нужно, когда вернусь ещё посмотрю. По поводу целочисленного деления, посмотри такой код (пояснения в коментариях):
Код
#!/usr/bin/perl -w
use strict;

# Объявляем две переменные типа double
my($x,$y) = (10.5,2.2);

# Выводим результат деления
print $x / $y . "\n";


# Переключаемся на целочисленную арифметику
use integer;
# Выводим результаты деления тех же операндов
print $x / $y . "\n";
# Отключаем целочисленную арифметику
no integer;


# Выводим результаты деления тех же операндов
print $x / $y . "\n";
 

Автор: Ivan Kolesnikov 5.6.2006, 15:47
Привет.
Цитата

Возникли проблеммы с определением дня недели по дате, встроенной функции не нашёл 

Есть такой модуль Date::Calc, так вот в нем есть функция Day_of_Week($year,$month,$day);
И вообще почитай про этот модуль (набири в командной строке perldoc Date::Calc.

Цитата

Не нашёл целочисленного деления (div  в паскале)

Да я раньше тоже искал smile и не нашел, но зато нашел отсечение дробной части, функция int, хотя в документации ее не рекомендуют использовать, но в твоем случае я думаю вполне подойдет.

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

Код

$data = [
  {
    number => 0, #номер месяца
    intervals => [
      {
        first_day_week => 1, #день недели 1-го дня интервала
        first_day => 1, #первый день интервала
        last_day => 20 #последний день интервала
      },
      ...
    ]
  },
  {
    ...
  }
  ...
]

Нужно определиться что делать если середина месяца не попадает в интервал, а края (или часть) попадает. Данная структура, удобна при пропуске интервала, если нужно выводить в таком случае весь месяц, то я бы убрал массив интервалов, заменив просто на элементы месяца.
Данную структуру не слишком сложно сформировать, а потом занялся бы отображением, на мой взгляд так проще smile
Но в принципе можно пойти и твоим методом, тогда формируй отдельные матрицы и напиши процедуру их склеивания smile 

Автор: nitr 5.6.2006, 17:32
всегда всем в помощь
http://search.cpan.org/search?query=calendar&mode=all
;) 

Автор: vumnik 5.6.2006, 19:51
korob2001
Интересный вариант! Не нужно, даже, отдельную функцию создавать.

Ivan Kolesnikov
Спасибо, что натолкнул на мысль. О структуре данных я совсем не подумал.  smile Предложенная тобой структура не совсем удобна. Не учитывает перекрывающиеся интервалы и день недели 1-го дня интервала там не к чему, его можно определять при выводе. Но мысль мне понравилась. 
Думаю сделать так: {'год'}{'месяц'}[день_начала_интервала, д...а+1, д...а+2, ..., день_конца_интервала] просто заполнять, просто выводить и проблемм с перекрывающимися интервалами не будет.
 

Автор: Ivan Kolesnikov 6.6.2006, 03:12
Привет!
vumnik, я просто привык разделять места обработки данных и отображения smile, и поэтому расчитал день недели заранее, чтобы в функции отображения (перевода в массив строк) уже не задумываться о том какой это день недели, нам уже все сказано, только отображай.

Предложенная тобой структура, мне не совсем понятна: что представляет собой массив, как понял я - это просто дни, которые нужно отобразить, тогда тебе придется для каждого дня вычислять его день недели (что по мне очень не оптимально) или писать дополнительный алгоритм проверки какой был предыдущий день. Мне кажется оперировать готовыми интервалами дней удобнее.

Цитата(vumnik @  6.6.2006,  00:51 Найти цитируемый пост)
Предложенная тобой структура не совсем удобна. Не учитывает перекрывающиеся интервалы

Что значит перекрывающиеся значения, какой должен быть ответ для таких интервалов: 5.01.2006-15.01.2006, 10.01.2006-20.01.2006, как мне кажется такие интервалы нужно объединять в один: 5.01.2006-20.01.2006 или ты хочешь как-нибудь по другому?
 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)