LINUX.ORG.RU

5 гласных в алфавитном порядке. Как лучше считать?

 ,


0

3

Наткнулся на лингвистический прикол: поиск слов, в которых есть все 5 гласных в алфавитном порядке. В случае английского это aeiou («y» не участвует, как дублирующая «i»), и таких слов довольно много. Для русского рассказывавший выбрал аиоуэ, и нашлась только «радиодуэль», которой нет в большинстве словарей.

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

Сделал так: из списка слов выбирается subset, в котором ровно по 1 гласной из каждой пары, затем регулярным выражением выбираются слова, где они в алфавитном порядке.

import re, zipfile, py7zr

def five_vowels(w): 
    return w.count('а') + w.count('я') == w.count('э') + w.count('е') == w.count('ы') + w.count('и') == w.count('у') + w.count('ю') == w.count('о') + w.count('ё') == 1

rex = '[^аеёиоуыэюя]*' + '[^аеёиоуыэюя]*'.join(['а*','е*','ё*','и*','о*','у*','ы*','э*','ю*','я*']) + '[^аеёиоуыэюя]*'
# '[^аеёиоуыэюя]*а*[^аеёиоуыэюя]*е*[^аеёиоуыэюя]*ё*[^аеёиоуыэюя]*и*[^аеёиоуыэюя]*о*[^аеёиоуыэюя]*у*[^аеёиоуыэюя]*ы*[^аеёиоуыэюя]*э*[^аеёиоуыэюя]*ю*[^аеёиоуыэюя]*я*[^аеёиоуыэюя]*', w)]

wordlist = zipfile.ZipFile('word_rus.zip').read('word_rus.txt').decode().split('\r\n')
subset = [w for w in wordlist if five_vowels(w)]
ordered = [ w for w in subset if re.fullmatch(rex, w)]
print(ordered)

wordlist = py7zr.SevenZipFile('russian-wordlist-inflections-1251.txt.7z').read(['russian-wordlist-inflections-1251.txt'])['russian-wordlist-inflections-1251.txt'].read().decode('cp1251').split('\n')
subset = [w for w in wordlist if five_vowels(w)]
ordered = [w for w in subset if re.fullmatch(rex, w)]
print(ordered)

Как-то можно это записать покороче?

★★★★★

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

Какой мрак, регекспы какие-то притащил, ну епрст.

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

PROHIBITED_PAIRS = ('ая', 'оё', 'ую', 'ыи', 'эе')
VOWELS = 'аеёиоуыэюя'
MASKS_LOW = {pair[0]: 1 << i for i, pair in enumerate(PROHIBITED_PAIRS)}
MASKS_HIGH = {pair[1]: 1 << i for i, pair in enumerate(PROHIBITED_PAIRS)}


with open('russian.txt') as f:
    for word in f:
        # Check that word contains one from each vowel pairs
        mask_low = mask_high = 0
        for char in word:
            mask_low |= MASKS_LOW.get(char, 0)
            mask_high |= MASKS_HIGH.get(char, 0)
        if mask_low | mask_high != 0b11111 or mask_low & mask_high:
            continue
        # Check that vowels have no duplicates and are in order:
        vowels = [v.lower() for v in word if v in VOWELS]
        if sorted(set(vowels), key=VOWELS.index) == vowels:
            print(word.rstrip())
t184256 ★★★★★
()
Ответ на: комментарий от t184256

Не bug-for-bug compatible, зато находит и с гласными в верхнем регистре:

PROHIBITED_PAIRS = ('ая', 'оё', 'ую', 'ыи', 'эе')
VOWELS = 'аеёиоуыэюя'
MASKS_LOW = {pair[0]: 1 << i for i, pair in enumerate(PROHIBITED_PAIRS)}
MASKS_HIGH = {pair[1]: 1 << i for i, pair in enumerate(PROHIBITED_PAIRS)}


with open('russian.txt') as f:
    for word in f:
        # Check that word contains one from each vowel pairs
        mask_low = mask_high = 0
        for char in word.rstrip().lower():
            mask_low |= MASKS_LOW.get(char, 0)
            mask_high |= MASKS_HIGH.get(char, 0)
        if mask_low | mask_high != 0b11111 or mask_low & mask_high:
            continue
        # Check that vowels have no duplicates and are in order
        vowels = [v.lower() for v in word if v in VOWELS]
        if sorted(set(vowels), key=VOWELS.index) == vowels:
            print(word.rstrip())
t184256 ★★★★★
()
Ответ на: комментарий от t184256

На comprehension’ах без import’ов и без загрузки всего файла в память, прям даже как будто настоящие let и chaining:

PAIRS = ('ая', 'оё', 'ую', 'ыи', 'эе')
VOWELS = 'аеёиоуыэюя'

words = (word.rstrip() for word in open('russian.txt')
         if (vowels := [v for v in word.lower() if v in VOWELS])
         if sum((x in vowels) ^ (y in vowels) for x, y in PAIRS) == len(PAIRS)
         if all(VOWELS.index(x) < VOWELS.index(y)
                for x, y in zip(vowels, vowels[1:])))

for word in words:
    print(word)
t184256 ★★★★★
()
Последнее исправление: t184256 (всего исправлений: 6)

Бессмысленность затеи конечно знатная, но раз уж такая пьянка, то в японском алфавите идет аиуэо. Впрочем и радиодуэль это вообще-то 2 иностранных слова вместе. Цель языка не в создании неперевариваемых норм, а как раз в оставлении только нужного путем отсева неблагозвучных слов. Так что это ты нашел английские слова radio и duel. В двух фактических словах может быть разнородный состав и это нормально. Но это все на уровне упражнений Кегеля - совершенно ненужное копание. В японском прицепить тою: можно дофига к чему и будет тебе сразу плюс две гласные. А в корейском одна лишь буква И обозначает дофига всего и является словом. К тому же там есть Ы. Чего не добавил Ы тогда? Все на запад тянет к недоразвитым языкам?

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

Че-то я перемудрил, можно же еще проще:

PAIRS = ('ая', 'оё', 'ую', 'ыи', 'эе')
VOWELS = 'аеёиоуыэюя'

words = (word.rstrip() for word in open('russian.txt')
         if (vowels := [v for v in word.lower() if v in VOWELS])
         if all((x in vowels) ^ (y in vowels) for x, y in PAIRS)
         if all(VOWELS.index(x) < VOWELS.index(y)
                for x, y in zip(vowels, vowels[1:])))

for word in words:
    print(word)
t184256 ★★★★★
()
Ответ на: комментарий от t184256

зато находит и с гласными в верхнем регистре

Не было необходимости, попались wordlist-ы приведённые к нижнему.

Поправка: это я так думал. Приводить было нужно.

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

Сбейте меня палкой

VOWELS, PAIRS = 'аеёиоуыэюя', ('ая', 'оё', 'ую', 'ыи', 'эе')

words = (word.rstrip() for word in open('russian.txt')
         if (vowels := [v for v in word.lower() if v in VOWELS])
         if all((x in vowels) ^ (y in vowels) for x, y in PAIRS)
         if sorted(set(vowels), key=VOWELS.index) == vowels)

for word in words: print(word)
t184256 ★★★★★
()
1. Вынимаем из слова гласные
2. Сортируем в алфавитном порядке
3. Если порядок не изменился, то 
3.1. Проверяем на наличие запрещенных пар
3.2. Если все хорошо, то это хорошее слово, пишем его в файл.

Например, хорошим будет слово из трех букв, посередине «у».

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

И после такого мне говорят что функциональщина это хорошо. Больше всего мне тут понравилось 0b11111. Жаль что конкурс не на C++, а то бы ещё тут было UB, пара утечек памяти, лапша с goto и магия на указателях. А так обошлись только магическими числами и глобальными переменными, т.к. всего остального python не умеет.

anonymous
()
(ns find-successive-chars
  (:require
   [clojure.java.io :as io]
   [clojure.string :as s]))

(defn successive-chars-regex
  "Returns a regex that matches a string containing (unicode) `chars` in order."
  [chars]
  (let [unicode-escaped-chars    (map #(format "\\u%04x" (int %)) chars)
        exclude-chars-subpattern (format "[^%s]*" (s/join unicode-escaped-chars))]
    (->> unicode-escaped-chars
         (reduce (fn [acc val]
                   (str acc val exclude-chars-subpattern)) exclude-chars-subpattern)
         re-pattern)))

(defn successive-chars?
  "Returns true if `string` contains (unicode) `chars` in order."
  [chars string]
  (boolean (re-matches (successive-chars-regex chars) string)))

(defn word-seq
  "Returns a sequence of (unicode) words in the `string`."
  [string]
  (re-seq #"\p{L}+" string))

(defn find-words
  "Returns a sequence of words in `file` that contain (unicode) `chars` in order."
  [file chars]
  (->> (line-seq (io/reader file))
       (keep word-seq)
       flatten
       (filter #(successive-chars? chars %))))
(find-words "/tmp/Звёздная пехота.txt" [\е \ч \н \о])
;; => ("вечно" "вечной" "величиной" "пессимистично" "вечно" "величиной" "безразлично" "вечной")
Nervous ★★★★★
()
Последнее исправление: Nervous (всего исправлений: 4)
Ответ на: комментарий от anonymous

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

И после такого мне говорят что функциональщина это хорошо.

А какая разница, после чего? У твоего умения читать есть моменты просветления?

t184256 ★★★★★
()
Ответ на: комментарий от LINUX-ORG-RU

Хотя если допускать частичное вхождение типа тарелки или/и вхождение пропусками типа лезвия то таких слов тыщи, выявлять их смысла нет. Вот 4 гласных подряд уже несколько сотен. Но нет ни одного слова где есть 5 гласных в алфавитном порядке подряд (даже с пропусками) и в обратном порядке тоже нет. А для 4 в обратном порядке всего пару сотен.

LINUX-ORG-RU ★★★★★
()
Последнее исправление: LINUX-ORG-RU (всего исправлений: 1)

из каждой пары а-я, о-ё, у-ю, ы-и, э-е должна быть только одна буква

А в чём «послабление»? Если встретилась старшая буква из пары, то дальше можно не проврерять, т.к. алфавитный порядок всё равно будет нарушен. Т.е. если вместо первой «а» идёт «я» то все эти слова сразу можно выбрасывать и т.д.

Или я что-то не так понял?

no-such-file ★★★★★
()
Последнее исправление: no-such-file (всего исправлений: 1)
Ответ на: комментарий от LINUX-ORG-RU

Но нет ни одного слова где есть 5 гласных в алфавитном порядке подряд

Если брать не только в именительном падеже и единственном числе, нашлось 162 штуки. С именами собственными — 169. Например, «застенчивому».

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

Не отбрасываются слова с буквами е, ё, ы, ю, я.

Они и так не отбрасываются, если требовать только 5 букв. Слова на «ыэюя» можно сразу выбросить, после них не может быть 5 гласных в нужном порядке.

Запрет на пары выглядит наоборот как усложнение, т.к. отваливаются слова например все слова начинающиеся на «и» в которых есть «ю». (я хз есть ли такие слова, но это точно запрет сильнее чем просто 5 гласных в нужном порядке).

no-such-file ★★★★★
()
Последнее исправление: no-such-file (всего исправлений: 4)
Ответ на: комментарий от no-such-file

Они и так не отбрасываются,

Формулировка задачи, на которую я наткнулся, требовала 5 гласных «аиоуэ». Учитывая, сколько в русском языке слов с «э», я решил условия ослабить.

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

требовала 5 гласных «аиоуэ»

Она требовала всех гласных в алфавите.

решил условия ослабить

Для русского языка требовать только 5 гласных уже ослабление. Чтобы ещё ослабить можно разрешить пропуски.

no-such-file ★★★★★
()
Ответ на: комментарий от question4

Взял это словарик

dron@gnu:~/Рабочий-стол/words$ lua test.lua  #прямой порядок
1:66559
2:58856
3:28974
4:5127
5:162
6:0
7:0
8:0
9:0
10:0
dron@gnu:~/Рабочий-стол/words$ lua test.lua  #обратный порядок 
1:56185
2:48482
3:19068
4:1554
5:9
6:0
7:0
8:0
9:0
10:0
dron@gnu:~/Рабочий-стол/words$ 

Код не покажу, он говня. На половину у тебя скопирован :) (хотя может где и неверно делаю, голова совсем не работает, хнык)

LINUX-ORG-RU ★★★★★
()
Последнее исправление: LINUX-ORG-RU (всего исправлений: 1)
Ответ на: комментарий от no-such-file

Для русского языка требовать только 5 гласных уже ослабление.

Подход такой, что существуют всего 5 гласных звуков («ы» считается вариантом «и» и т.п.; тема для филологов довольно холиварная, как я понимаю). Поэтому всего 5 букв.

Если задача изначально английская, то там очень много прилагательных и наречий с «Y» на конце, поэтому понятно, почему искали без неё. Но для русского языка отказываться от «е» неправильно. Поэтому я взял условие «любая из пары».

question4 ★★★★★
() автор топика
Ответ на: комментарий от no-such-file

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

Irma ★★
()
Ответ на: комментарий от LINUX-ORG-RU

Код не покажу, он говня.

Хотя чего стесняться, все свои, и так периодически позорный код выкладываю, всё равно уже наигрался :D

os.execute('git clone https://github.com/danakt/russian-words.git')
os.execute('iconv -f WINDOWS-1251 -t UTF-8 ./russian-words/russian.txt > russian.txt')
local file = io.open('russian.txt')
for max_max=1,10 do
    local cnt = 0
    file:seek('set')
    for word in file:lines() do
        (function()
           if word:find('%s') then return end
           if (({word:gsub('а','а')})[2] + ({word:gsub('я','я')})[2]) <= 1 and
              (({word:gsub('о','о')})[2] + ({word:gsub('ё','ё')})[2]) <= 1 and
              (({word:gsub('у','у')})[2] + ({word:gsub('ю','ю')})[2]) <= 1 and
              (({word:gsub('ы','ы')})[2] + ({word:gsub('и','и')})[2]) <= 1 and
              (({word:gsub('э','э')})[2] + ({word:gsub('е','е')})[2]) <= 1 then
              local rule = {'а','е','ё','и','о','у','ы','э','ю','я'}
              --local rule = {'я','ю','э','ы','у','о','и','ё','е','а'}
              local outs = { }
              for i,x in ipairs(rule) do
                  local v = (word:find(x))
                  if v then
                    outs[#outs+1] = v
                  end
              end
              if #outs >= max_max then
                 local ok = true
                 local vx = 0
                 for i=1,#outs do
                     if vx > outs[i] then
                        ok = false
                        break
                     end
                     vx = outs[i]
                 end
                 if ok then
                    --print(word,cnt)
                   cnt = cnt + 1
                 end
              end
           end
        end)()
    end
    print(max_max..':'..cnt)
end
LINUX-ORG-RU ★★★★★
()
Ответ на: комментарий от t184256
In [8]: FILE = 'russian.txt'
In [9]: VOWELS = 'аеёиоуыэюя'
   ...: with open(FILE, encoding='cp1251') as fp:
   ...:   for word in filter(None, map(str.strip, fp)):
   ...:     vovels = [c for c in word if c in VOWELS]
   ...:     if len(vovels) == 5 and len(set(vovels)) == len(vovels) and sorted(vovels) == vovels:
   ...:       print(word)

Высер:

...
шеститомную
шеститонную
шпаклевочную
шпатлевочную
штабелировку
штабелирую
штабелируют
штабелируя
штангенциркулю
штангенциркуля
штангенциркулям
штангенциркулях
щенившуюся
щерившуюся
щетинозубым
щетинозубых

Напомнило эпичную тему про яблоконя от @kompospec’a

rtxtxtrx ★★
()
Последнее исправление: rtxtxtrx (всего исправлений: 4)
Ответ на: комментарий от ya-betmen

Там 5 гласных идущих по порядку от е до ю, но это не совсем то что нужно, я дальше пары предложений первого абзаца не читаю… Пусть другие чет напишут, а то неинтересно будет

anonymous
()

аиоуэ

Не совсем, но почти

(find-words "/tmp/Звёздная пехота.txt" [\а \о \у \и \е])
;; => ("равнодушием" "малодушие")
(find-words "/tmp/Звёздная пехота.txt" "аеиоа")
;; => ("натренирован")
Nervous ★★★★★
()
Последнее исправление: Nervous (всего исправлений: 2)

Мда, почитал варианты решений. Я конечно понимаю что ЛОР для мидлов и шизов типа Ловсана, но не настолько же.

Никто не додумался перевести гласные в массив ASCII кодов (int) и сравнивать числа. Т. е. каждое слово проверять до тех пор пока текущий ASCII код символа есть в массиве кодов гласных и больше предыдущего попадающего в массив. Если меньше - прерываем проверку тк слово не подходит.

Простейший алгоритм с линейной сложностью, один цикл, одно условие if, одна временная переменная. Все.

Obezyan
()
Ответ на: комментарий от ya-betmen

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

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

перевести гласные в массив ASCII кодов (int) и сравнивать числа. Т. е. каждое слово проверять до тех пор пока текущий ASCII код символа есть в массиве кодов гласных и больше предыдущего попадающего в массив.

Коды не соответствуют алфавитному порядку — «ё» выпадает. Нужно изобретать свою нумерацию. Или при сравнении каждый раз искать символ в массиве из 10, что и делается.

А выход из цикла по результатам сравнения символов в примерах есть, вроде.

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

Никто не додумался перевести гласные в массив ASCII кодов (int) и сравнивать числа

Оно не имеет смысла. a < z True

массив ASCII кодов

юникод в ASCII. В Java, JS, Python… unicode codepoints, а не ASCII. Это в похапе строки - это тупо массив сhar’ов (как раз ASCII вмещается)

Простейший алгоритм с линейной сложностью, один цикл, одно условие if, одна временная переменная. Все.

Нет. Сама задача бред.

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

Так юникод же?

Для русского и английского алфавита юникод избыточен, достаточно ASCII. Но если в каком-то языке проще получить юникод числом то ради бога. Суть в переводе задачи сравнения букв в задачу сравнения чисел. Конкретная кодировка вторичная.

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

Коды не соответствуют алфавитному порядку — «ё»

Не проверял, поверю на слово, под рукой только телефон. Помню только что в англ алфавите все точно по порядку.

В любом случае добавить доп условие на одну букву проще чем то что выше городили.

Obezyan
()