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)
Ответ на: комментарий от Obezyan

Для русского и английского алфавита юникод избыточен, достаточно ASCII

Не хотите пердолиться с юникодом — будете пердолиться с кодировками %)

Суть в переводе задачи сравнения букв в задачу сравнения чисел

Но зачем что-то там сравнивать вручную, если уже есть матёрый DSL для поиска паттернов в последовательностях символов? Проще написать поиск на нём.

Зачем переизобретать регулярки? То, что получится переизобрести за вменяемое время, всё равно будет более скудно, сыро и бажно, чем существующие, проверенные и широко используемые реализации. А при изменении требований гораздо проще допилить одну регулярку, чем перехреначивать кишки кастомного байтолюбного алгоритма.

Разве что если ресурсы скромные, а требования 1) ещё скромнее 2) стабильные (ха-ха). Ну или джаст фор фан — что, собственно, здесь и происходит.

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

Еще один не читал условие про пары гласных.

Наши решения тебе чем не линейные? Если слова надо печатать, только если они подходят, а входной поток не seek’абельный, то для N слов длиной M меньше O(N * M) по операциям и O(M) по памяти не сделаешь.

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

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

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

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

Я не буду мучаться писать код с телефона чтобы удовлетворить ваше любопытство. Алгоритм простой и описан словами, этого достаточно.

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

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

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

Зачем переизобретать регулярки?

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

Ну или джаст фор фан — что, собственно, здесь и происходит.

Вы сами ответили на выдвинутые вами тезисы :)

Я лишь озвучил правильное (одно из) общее решение в провакационной форме в надежде что клюнет какой-нибудь толстолобый пятизнак или Ловсанчик. Пятизнак не разочаровал.

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

Алгоритм простой и описан словами, этого достаточно.

В том-то и дело, что ты тупишь и не догоняешь, что нет. И, видимо, пока не начнешь писать, не прозреешь. Давай, гуру, код в студию.

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

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

x='[^аяоёуюыиэе]*'; zgrep -Eio "^${x}[ая]${x}[оё]${x}[ую]${x}[ыи]${x}[эе]${x}#" ~/misc/dict/lopatin\#.txt.gz
автолюбитель#
благодушие#
благозвучие#
братолюбие#
властолюбие#
за отсутствием#
карпозубые#
малодушие#
малоумие#
правдолюбие#
правосудие#
празднолюбие#
прямодушие#
равнодушие#
самолюбие#
самоучитель#
самочувствие#
слабодушие#
слабоумие#
славолюбие#
сладкозвучие#
сластолюбие#
чадолюбие#

Другой словарик с родами и склонениями выдал ещё такие прикольные:

нравоучитель
страннолюбие
самоубивец

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

anonymous
()
#!/usr/bin/perl -wn
# cat russian.txt | perl filter.pl

use utf8;
use strict;
use open ':std', ':encoding(UTF-8)';

my $vowels = 'аеёиоуыэюя';

next if eval "y/$vowels//" < 7;

my $matched_vowels = join '', m{[$vowels]}g;

my $no_dupes = eval "\$matched_vowels =~ y/$vowels//sr";

print if $vowels =~ m{$no_dupes};

Нате, ребята. Поиграйтесь. Находит все слова с гласными идущими по алфавиту, без пропусков. Повторы разрешены.

perl5_guy ★★★★★
()

Присоединюсь к конкурсу, сразу скажу, что раз цель покороче и покрасивее, то не парюсь особо по производительности

import re
with open('russian.txt') as f:
    for word in f:
        substr = re.sub('[^аяоёуюыиэе]', '', word)
        if re.match('^[ая][оё][ую][ыи][эе]$', substr):
            print(word)
Скорее всего можно в одну регулярку свернуть всё и будет лучше, но код станет труднее понимать. Да, для полного решения надо ещё word к нужному регистру приводить и кодировки подгонять, но у меня файлик уже в utf8 и в нижнем регистре

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

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

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

Никто не додумался

Додумался, но лень. Конкретно «сравнивать числа» плохая идея, но в целом да, нужен конечный автомат на 5 состояний +1 состояние успеха и 1 состояние неуспеха. На каждом состоянии либо переходим в следующее (если нашли букву), либо сразу на неуспех. Дошли до конца/успеха – слово подходит.

PS: как тут уже справедливо заметили, пока ты будешь с этим пердолиться, посоны уже сделали на регулярках и sort.

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

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

Выше я писал:

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

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

но в целом да, нужен конечный автомат на 5 состояний +1 состояние успеха и 1 состояние неуспеха.

Спасибо, вы вернули веру в этот форум, а то иногда подумываю уйти отсюда. Вы полностью понимаете суть описанного мною подхода.

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

В очередной раз повторю что это зависит от языка, т. е. от реализации. Я просто привёл ещё одно решение помимо очевидных всем регулярных выражений.

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

Ты либо пиши более конкретное ТЗ, либо объясняй что не нравится. По твоему посту понятно что для тебя буквы а-я, о-ё, у-ю, ы-и, э-е равны, а в каком именно алфавитном порядке (по первой или второй букве а может вообще по средней позиции между ними) это уже тебе определиться надо.

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

Ты либо пиши более конкретное ТЗ, либо объясняй что не нравится.

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

Что непонятно? Гласные в алфавитном порядке. Если есть «е» — она вторая или первая (если нет «а»). Если есть «э» — она 3-я от конца, если есть «ю» и «я».

А в алфавитном порядке несмягчающих — это совсем просто, тут и спрашивать нечего.

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

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

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

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

Куда конкретнее-то? Гласные должны быть в алфавитном порядке. Не по первой букве пар, не по второй, не по средней позиции, не согласные, не цифры, не небо и не Аллах. Гласные, содержащиеся в слове должны быть расположены в нем в алфавитном порядке.

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

Прикол №2: поиск слов с наибольшим количеством согласных в алфавитном порядке.

Я смотрю, уже пора песать универсальный механизм для различных поисков. Извольте:

(defn re-some-matches
  "Returns true if any of `regexes` matches `string`."
  [regexes string]
  (boolean (some #(re-matches % string) regexes)))

(defn file-word-seq
  "Returns a sequence of words in `file`, matched by `word-regex`."
  [file word-regex]
  (->> (line-seq (io/reader file))
       (keep #(re-seq word-regex %))
       flatten))

(defn find-words
  "Returns a sequence of words in `file` that match at least one of
  `regexes`."
  ([regexes file]
   (find-words regexes file #"[\p{L}\p{N}]+"))
  ([regexes file word-regex]
   (->> (file-word-seq file word-regex)
        (filter #(re-some-matches regexes %)))))

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

(def successive-russian-vowels-regexes
  "Match words with 5 (Russian) vowels in alphabetical order."
   ;; add more character sequences --- by hand or generate
   (let [vowel-variants ["аеиоу" "аеуюя"]]
     (map successive-chars-regex vowel-variants)))

(find-words successive-russian-vowels-regexes "/tmp/Звёздная пехота.txt")
;; => ("знаменитому" "экзаменующийся")
Nervous ★★★★★
()
Последнее исправление: Nervous (всего исправлений: 1)
Ответ на: комментарий от dataman

как использовать пример?

Очень просто:

  1. Ставим емакс, CIDER, кложу или бабашку
  2. Кладём кот в файлик, текст в другой файлик
  3. Запускаем REPL, эвалуируем кот
  4. ???
  5. Результаты
Nervous ★★★★★
()
Ответ на: комментарий от dataman

Ну ладно, так и быть — одним куском. Только сегодня, зато совершенно бесплатно и без смс. Итааааак… всего три шага к успеху!

  1. Сохранить кот в файлик, chmod a+x файлик
  2. Скачать бабашку — самодостаточный бинарник, положить её в $PATH
  3. Запустить: ./файлик текстовый-файлик символы [символы ...]

Пример:

$ ./find-words 
Find words in text files that contain given char sequence(s).
Usage: find-words <text-file> <char-sequence> [char-sequence ...]

$ ./find-words /tmp/Звёздная\ пехота.txt аеиоу аеуюя
знаменитому
экзаменующийся
#!/usr/bin/env bb

;; -*- mode: clojure -*-

(ns find-words
  (:require
   [babashka.fs :as fs]
   [clojure.java.io :as io]
   [clojure.string :as s]))

(defn successive-chars-regex
  "Returns a regex that matches a string containing given (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 re-some-matches
  "Returns true if any of `regexes` matches `string`."
  [regexes string]
  (boolean (some #(re-matches % string) regexes)))

(defn file-word-seq
  "Returns a sequence of words in `file`, matched by `word-regex`."
  [file word-regex]
  (->> (line-seq (io/reader file))
       (keep #(re-seq word-regex %))
       flatten))

(defn find-words
  "Returns a sequence of words in `file` that match at least one of
  `regexes`."
  ([regexes file]
   (find-words regexes file #"[\p{L}\p{N}]+"))
  ([regexes file word-regex]
   (->> (file-word-seq file word-regex)
        (filter #(re-some-matches regexes %)))))


(let [[text-file & char-seqs] *command-line-args*]
  (if (or (not text-file)
          (empty? char-seqs))
    (println "Find words that contain given char sequence(s) in text files.\nUsage:"
             (str "./" (fs/file-name *file*))
             "<text-file> <char-sequence> [char-sequence]")
    (try
      (->> text-file
           (find-words (map successive-chars-regex char-seqs))
           (map println)
           doall)
      (catch Exception e (println (.getMessage e))))))
Nervous ★★★★★
()
Последнее исправление: Nervous (всего исправлений: 12)