Научить говорит Алису нормальные окончания

Использование системы в различных ситуациях, вопросы программирования сценариев.

Модератор: immortal

Ответить
alekszander
Сообщения: 178
Зарегистрирован: Вс янв 05, 2014 5:37 am
Благодарил (а): 10 раз
Поблагодарили: 17 раз

Научить говорит Алису нормальные окончания

Сообщение alekszander » Вт апр 26, 2016 9:43 am

В общем озадачился тем что бы Алиса проговаривала нормальные окончания. В основном тестировал на погоде. Все отлично, но выполнять можно только на одном показатели (температура, влажность или скорость ветра). Уважаемые гуру пхп помогите. Есть ли возможность прикрутить к мдм. Если будут вопросы, готов ответить.
Код вызова скрипта(на примере влажности)Показать

Код: Выделить всё

runScript("humidity_string",array(
"hs13"=>ceil(gg("Vladivostok.mcD0_Humidity_0")),"hst13"=>"Vladivostok.mcD0_Humidity_0_text", 
"hs20"=>ceil(gg("Vladivostok.mcD0_Humidity_1")),"hst20"=>"Vladivostok.mcD0_Humidity_1_text",
"hs21"=>ceil(gg("Vladivostok.mcD0_Humidity_2")),"hst21"=>"Vladivostok.mcD0_Humidity_2_text",
"hs22"=>ceil(gg("Vladivostok.mcD0_Humidity_3")),"hst22"=>"Vladivostok.mcD0_Humidity_3_text"
));
СкриптПоказать

Код: Выделить всё

if (
//влажность
$params['hs13']=="" or 
$params['hst13']=="" or
$params['hs20']=="" or
$params['hst20']=="" or
$params['hs21']=="" or
$params['hst21']=="" or
$params['hst22']=="" or
$params['hst22']=="" 
) { return; }

$hs13=$params['hs13'];
$hst13=$params['hst13'];
$hsr13=number2string($hs13);
sg("$hst13",$hsr13);

$hs20=$params['hs20'];
$hst20=$params['hst20'];
$hsr20=number2string($hs20);
sg("$hst20",$hsr20);

$hs21=$params['hs21'];
$hst21=$params['hst21'];
$hsr21=number2string($hs21);
sg("$hst21",$hsr21);

$hs22=$params['hs22'];
$hst22=$params['hst22'];
$hsr22=number2string($hs22);
sg("$hst22",$hsr22);

function number2string($number) {
    
    // обозначаем словарь в виде статической переменной функции, чтобы 
    // при повторном использовании функции его не определять заново
    static $dic = array(
    
        // словарь необходимых чисел
        array(
            -2    => 'две',
            -1    => 'одна',
            1    => 'один',
            2    => 'два',
            3    => 'три',
            4    => 'четыре',
            5    => 'пять',
            6    => 'шесть',
            7    => 'семь',
            8    => 'восемь',
            9    => 'девять',
            10    => 'десять',
            11    => 'одиннадцать',
            12    => 'двенадцать',
            13    => 'тринадцать',
            14    => 'четырнадцать' ,
            15    => 'пятнадцать',
            16    => 'шестнадцать',
            17    => 'семнадцать',
            18    => 'восемнадцать',
            19    => 'девятнадцать',
            20    => 'двадцать',
            30    => 'тридцать',
            40    => 'сорок',
            50    => 'пятьдесят',
            60    => 'шестьдесят',
            70    => 'семьдесят',
            80    => 'восемьдесят',
            90    => 'девяносто',
            100    => 'сто',
            200    => 'двести',
            300    => 'триста',
            400    => 'четыреста',
            500    => 'пятьсот',
            600    => 'шестьсот',
            700    => 'семьсот',
            800    => 'восемьсот',
            900    => 'девятьсот'
        ),
        
        // словарь порядков со склонениями для плюрализации
        array(
            array('процент', 'процента', 'процентов'),
            ),
        
        // карта плюрализации
        array(
            2, 0, 1, 1, 1, 2
        )
    );
    
    // обозначаем переменную в которую будем писать сгенерированный текст
    $string = array();
    
    // дополняем число нулями слева до количества цифр кратного трем,
    // например 1234, преобразуется в 001234
    $number = str_pad($number, ceil(strlen($number)/3)*3, 0, STR_PAD_LEFT);
    
    // разбиваем число на части из 3 цифр (порядки) и инвертируем порядок частей,
    // т.к. мы не знаем максимальный порядок числа и будем бежать снизу
    // единицы, тысячи, миллионы и т.д.
    $parts = array_reverse(str_split($number,3));
    
    // бежим по каждой части
    foreach($parts as $i=>$part) {
        
        // если часть не равна нулю, нам надо преобразовать ее в текст
        if($part>0) {
            
            // обозначаем переменную в которую будем писать составные числа для текущей части
            $digits = array();
            
            // если число треххзначное, запоминаем количество сотен
            if($part>99) {
                $digits[] = floor($part/100)*100;
            }
            
            // если последние 2 цифры не равны нулю, продолжаем искать составные числа
            // (данный блок прокомментирую при необходимости)
            if($mod1=$part%100) {
                $mod2 = $part%10;
                $flag = $i==1 && $mod1!=11 && $mod1!=12 && $mod2<3 ? -1 : 1;
                if($mod1<20 || !$mod2) {
                    $digits[] = $flag*$mod1;
                } else {
                    $digits[] = floor($mod1/10)*10;
                    $digits[] = $flag*$mod2;
                }
            }
            
            // берем последнее составное число, для плюрализации
            $last = abs(end($digits));
            
            // преобразуем все составные числа в слова
            foreach($digits as $j=>$digit) {
                $digits[$j] = $dic[0][$digit];
            }
            
            // добавляем обозначение порядка или валюту
            $digits[] = $dic[1][$i][(($last%=100)>4 && $last<20) ? 2 : $dic[2][min($last%10,5)]];
            
            // объединяем составные числа в единый текст и добавляем в переменную, которую вернет функция
            array_unshift($string, join(' ', $digits));
        }
    }
    
    // преобразуем переменную в текст и возвращаем из функции, ура!
    return join(' ', $string);
} 
Вопрос в общем следующий как добавить еще один параметр что бы запускать одну функцию на несколько параметров (влажность, ветер, давление). В принципе при должном использовании можно добавить словарей ещё.
skysilver
Сообщения: 3006
Зарегистрирован: Чт авг 21, 2014 8:28 am
Откуда: Киров, Россия
Благодарил (а): 400 раз
Поблагодарили: 1754 раза
Контактная информация:

Re: Научить говорит Алису нормальные окончания

Сообщение skysilver » Чт апр 28, 2016 12:47 pm

Как-то уж больно кудряво у вас. ;) Я использую вот такую функцию:

Код: Выделить всё

// Склонение числительных
// string - само число
// ch1 - час, день, год, месяц
// ch2 - часа, дня, года, месяца
// ch3 - часов, дней, лет, месяцев
// и т.д.
function chti($string, $ch1, $ch2, $ch3)
{
    $ff = Array('0','1','2','3','4','5','6','7','8','9');
    
    if ( substr($string,-2, 1) == 1 AND strlen($string) > 1 ) $ry = array("0 $ch3","1 $ch3","2 $ch3","3 $ch3" ,"4 $ch3","5 $ch3","6 $ch3","7 $ch3","8 $ch3","9 $ch3");
     else $ry = array("0 $ch3","1 $ch1","2 $ch2","3 $ch2","4 $ch2","5 $ch3","6 $ch3","7 $ch3","8 $ch3","9 $ch3");
    
    $string1 = substr($string,0,-1).str_replace($ff, $ry, substr($string,-1,1));
    
    return $string1;
} 
Варианты применения:

Код: Выделить всё

say('Московское время '.chti($h,'час','часа','часов').' ровно.', 1);
say("Сейчас ".chti((int)date('G', time()),'час','часа','часов')." ".chti((int)date('i', time()),'минута','минуты','минут'), 1);
setGlobal('ColdWaterCounter.nextCalibration', chti($y, 'год ', 'года ', 'лет ').chti($m, 'месяц ', 'месяца ', 'месяцев ').chti($d, 'день ', 'дня ', 'дней '));
$dTimeSay = 'Вы отсутствовали '.chti($dHour,'час','часа','часов').' '.chti($dMin,'минуту','минуты','минут');
$metcast = ' Температура воздуха ' . chti((int)$temp,'градус','градуса','градусов') . '.'; 
Возможно, код не идеальный, и будут ошибки склонения, но мне пока что хватает этого варианта.
MajorDoMo (GitHub) на Cubietruck. ОС Debian 7 (wheezy) (kernel 3.4.105) с переносом на HDD.
Мой CONNECT | Блоги | Telegram
Аватара пользователя
m-malva
Сообщения: 291
Зарегистрирован: Чт фев 26, 2015 1:38 am
Откуда: Санкт-Петербург
Благодарил (а): 16 раз
Поблагодарили: 66 раз
Контактная информация:

Re: Научить говорит Алису нормальные окончания

Сообщение m-malva » Чт апр 28, 2016 2:08 pm

Код: Выделить всё

function pForm($n, $form1, $form2, $form5) {
    $n = abs($n) % 100;
    $n1 = $n % 10;
    if ($n > 10 && $n < 20) return $form5;
    if ($n1 > 1 && $n1 < 5) return $form2;
    if ($n1 == 1) return $form1;
    return $form5;
}

Код: Выделить всё

say ('Сегодня было отправлено '.$Sum.' СМС '.pForm($Sum, 'сообщение', 'сообщения', 'сообщений'),1);
Ответить