Решение на Text Info от Мартин Георгиев

Обратно към всички решения

Към профила на Мартин Георгиев

Резултати

  • 15 точки от тестове
  • 0 бонус точки
  • 15 точки общо
  • 15 успешни тест(а)
  • 0 неуспешни тест(а)

Код

pub struct TextInfo {
char_cnt: usize,
latin_cnt: usize,
cyrillic_cnt: usize,
words_cnt: usize,
questions_cnt: usize,
exclamations_cnt: usize,
dots_cnt: usize
}
impl TextInfo {
pub fn init() -> Self {
TextInfo {
char_cnt: 0,
latin_cnt: 0,
cyrillic_cnt: 0,
words_cnt: 0,
questions_cnt: 0,
exclamations_cnt: 0,
dots_cnt: 0
}
}
pub fn new(s: &str) -> Self {
TextInfo::generate_info(s)
}

Индирекцията между new, init, и generate_info ми е малко странна, и не съм сигурен доколко има практическа полза. Ако извикаш извън този код TextInfo::init(), ще получиш някакъв TextInfo обект, който ще е на практика безполезен -- всичките му стойности ще са 0, и няма да има методи да се мутира. Property-тата не са маркирани като pub, което значи, че не можеш и да мутираш този TextInfo.

По-малко индирекция (което би довело до по-лесен за четене код) би било new функцията да си алокира TextInfo, и да му генерира стойностите за дадения низ. Разбира се, процеса на генериране може да е в отделна функция generate, но тя би могла да бъде метод на структурата, а не статична функция.

Тоест, new би изглеждало така:

pub fn new(s: &str) -> Self {
   let mut ti = TextInfo { ... };
   ti.generate(s);
   ti
}

Или, ако смяташ, че искаш да преизползваш една TextInfo структура за няколко низа, можеш да направиш нещо такова:

let mut ti = TextInfo::init();

ti.generate_info("foo");
println!("{}", ti.char_count()); // => 3

ti.generate_info("foo bar");
println!("{}", ti.char_count()); // => 7

Разбира се, ако имаше контрол над интерфейса, то вместо init() е по-добре да ползваш името new(), за да паснеш на конвенциите на Rust.

Аз не бих написал интерфейса по този начин, намирам го за сравнително тежък (отделна инициализация, която трябва да е на mutable, и извикване). Но нали, вариант е. По-нататък ще видим как може да TextInfo да си държи &str вместо String, и това ще си е доста ефективен вариант за имплементиране на нещо подобно.

pub fn char_count(&self) -> usize {
self.char_cnt
}
pub fn alphabetic_count(&self) -> usize {
self.cyrillic_cnt + self.latin_cnt
}
pub fn latin_letter_count(&self) -> usize {
self.latin_cnt
}
pub fn cyrillic_letter_count(&self) -> usize {
self.cyrillic_cnt
}
pub fn word_count(&self) -> usize {
self.words_cnt
}
pub fn sentence_count(&self) -> usize {
self.questions_cnt +
self.exclamations_cnt +
self.dots_cnt
}
pub fn emotion(&self) -> String {
if self.questions_cnt > self.exclamations_cnt &&
self.questions_cnt > self.dots_cnt {
return String::from("🤔");
}
if self.exclamations_cnt > self.questions_cnt &&
self.exclamations_cnt > self.dots_cnt {
return String::from("😮");
}
String::from("😐")
}
}
impl TextInfo {
fn generate_info(s: &str) -> Self {
let mut text_info = TextInfo::init();
let mut is_word: bool = false;
let mut no_mark_chain: bool = false;
for ch in s.chars() {
text_info.char_cnt += 1;
if TextInfo::is_alphabetic(ch) {
if TextInfo::is_latin(ch){
text_info.latin_cnt += 1;
} else if TextInfo::is_cyrillic(ch) {
text_info.cyrillic_cnt += 1;
}
if !is_word {
text_info.words_cnt += 1;
is_word = true;
}
} else {
is_word = false;
}
if TextInfo::is_sentence_mark(ch) {
if no_mark_chain {
no_mark_chain = false;
match ch {
'!' => text_info.exclamations_cnt += 1,
'.' => text_info.dots_cnt += 1,
'?' => text_info.questions_cnt += 1,
_ => ()
}
}
} else {
no_mark_chain = true;
}
}
text_info
}
fn is_sentence_mark(ch: char) -> bool {
ch == '!' || ch == '?' || ch == '.'
}
fn is_alphabetic(ch: char) -> bool {
TextInfo::is_latin(ch) || TextInfo::is_cyrillic(ch)
}
fn is_latin(ch: char) -> bool {
(ch >= 'a' && ch <= 'z') ||
(ch >= 'A' && ch <= 'Z')
}
fn is_cyrillic(ch: char) -> bool {
(ch >= 'а' && ch <= 'я') ||
(ch >= 'А' && ch <= 'Я')
}
}

Лог от изпълнението

Compiling solution v0.1.0 (file:///tmp/d20171026-5817-3wrvq4/solution)
    Finished dev [unoptimized + debuginfo] target(s) in 3.7 secs
     Running target/debug/deps/solution-f5dd4e94aa395cae

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

     Running target/debug/deps/solution_test-c3b431457e2a7a27

running 15 tests
test solution_test::test_alpha_count ... ok
test solution_test::test_alpha_count_2 ... ok
test solution_test::test_char_count ... ok
test solution_test::test_cyrillic_letter_count ... ok
test solution_test::test_emotions ... ok
test solution_test::test_emotions_repeated_punctuation ... ok
test solution_test::test_empty_string ... ok
test solution_test::test_latin_letter_count ... ok
test solution_test::test_sentence_count ... ok
test solution_test::test_sentence_count_2 ... ok
test solution_test::test_triple_dots_count ... ok
test solution_test::test_unicode_char_count ... ok
test solution_test::test_word_count ... ok
test solution_test::test_word_count_2 ... ok
test solution_test::test_word_count_3 ... ok

test result: ok. 15 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

   Doc-tests solution

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

История (2 версии и 3 коментара)

Мартин качи първо решение на 24.10.2017 00:47 (преди почти 8 години)

Мартин качи решение на 24.10.2017 20:18 (преди почти 8 години)

pub struct TextInfo {
char_cnt: usize,
latin_cnt: usize,
cyrillic_cnt: usize,
words_cnt: usize,
questions_cnt: usize,
exclamations_cnt: usize,
dots_cnt: usize
}
impl TextInfo {
pub fn init() -> Self {
TextInfo {
char_cnt: 0,
latin_cnt: 0,
cyrillic_cnt: 0,
words_cnt: 0,
questions_cnt: 0,
exclamations_cnt: 0,
dots_cnt: 0
}
}
pub fn new(s: &str) -> Self {
TextInfo::generate_info(s)
}

Индирекцията между new, init, и generate_info ми е малко странна, и не съм сигурен доколко има практическа полза. Ако извикаш извън този код TextInfo::init(), ще получиш някакъв TextInfo обект, който ще е на практика безполезен -- всичките му стойности ще са 0, и няма да има методи да се мутира. Property-тата не са маркирани като pub, което значи, че не можеш и да мутираш този TextInfo.

По-малко индирекция (което би довело до по-лесен за четене код) би било new функцията да си алокира TextInfo, и да му генерира стойностите за дадения низ. Разбира се, процеса на генериране може да е в отделна функция generate, но тя би могла да бъде метод на структурата, а не статична функция.

Тоест, new би изглеждало така:

pub fn new(s: &str) -> Self {
   let mut ti = TextInfo { ... };
   ti.generate(s);
   ti
}

Или, ако смяташ, че искаш да преизползваш една TextInfo структура за няколко низа, можеш да направиш нещо такова:

let mut ti = TextInfo::init();

ti.generate_info("foo");
println!("{}", ti.char_count()); // => 3

ti.generate_info("foo bar");
println!("{}", ti.char_count()); // => 7

Разбира се, ако имаше контрол над интерфейса, то вместо init() е по-добре да ползваш името new(), за да паснеш на конвенциите на Rust.

Аз не бих написал интерфейса по този начин, намирам го за сравнително тежък (отделна инициализация, която трябва да е на mutable, и извикване). Но нали, вариант е. По-нататък ще видим как може да TextInfo да си държи &str вместо String, и това ще си е доста ефективен вариант за имплементиране на нещо подобно.

pub fn char_count(&self) -> usize {
self.char_cnt
}
pub fn alphabetic_count(&self) -> usize {
self.cyrillic_cnt + self.latin_cnt
}
pub fn latin_letter_count(&self) -> usize {
self.latin_cnt
}
pub fn cyrillic_letter_count(&self) -> usize {
self.cyrillic_cnt
}
pub fn word_count(&self) -> usize {
self.words_cnt
}
pub fn sentence_count(&self) -> usize {
self.questions_cnt +
self.exclamations_cnt +
self.dots_cnt
}
pub fn emotion(&self) -> String {
if self.questions_cnt > self.exclamations_cnt &&
self.questions_cnt > self.dots_cnt {
- String::from("😮")
- } else if self.exclamations_cnt > self.questions_cnt &&
- self.exclamations_cnt > self.dots_cnt {
- String::from("🤔")
- }
- else {
- String::from("😐")
- }
+ return String::from("🤔");
+ }
+ if self.exclamations_cnt > self.questions_cnt &&
+ self.exclamations_cnt > self.dots_cnt {
+ return String::from("😮");
+ }
+
+ String::from("😐")
}
}
impl TextInfo {
fn generate_info(s: &str) -> Self {
let mut text_info = TextInfo::init();
let mut is_word: bool = false;
let mut no_mark_chain: bool = false;
for ch in s.chars() {
text_info.char_cnt += 1;
if TextInfo::is_alphabetic(ch) {
if TextInfo::is_latin(ch){
text_info.latin_cnt += 1;
} else if TextInfo::is_cyrillic(ch) {
text_info.cyrillic_cnt += 1;
}
if !is_word {
text_info.words_cnt += 1;
is_word = true;
}
} else {
is_word = false;
}
if TextInfo::is_sentence_mark(ch) {
if no_mark_chain {
no_mark_chain = false;
match ch {
'!' => text_info.exclamations_cnt += 1,
'.' => text_info.dots_cnt += 1,
'?' => text_info.questions_cnt += 1,
_ => ()
}
}
} else {
no_mark_chain = true;
}
}
text_info
}
fn is_sentence_mark(ch: char) -> bool {
ch == '!' || ch == '?' || ch == '.'
- }
-
- pub fn print(&self) {
- println!("chars: {:?}", self.char_count());
- println!("alphabetic: {:?}", self.alphabetic_count());
- println!("latin: {:?}", self.latin_letter_count());
- println!("cyrillic: {:?}", self.cyrillic_letter_count());
- println!("words: {:?}", self.word_count());
- println!("sentences: {:?}", self.sentence_count());
- println!("emotion: {:?}", self.emotion());
}
fn is_alphabetic(ch: char) -> bool {
TextInfo::is_latin(ch) || TextInfo::is_cyrillic(ch)
}
fn is_latin(ch: char) -> bool {
(ch >= 'a' && ch <= 'z') ||
(ch >= 'A' && ch <= 'Z')
}
fn is_cyrillic(ch: char) -> bool {
(ch >= 'а' && ch <= 'я') ||
(ch >= 'А' && ch <= 'Я')
}
}