Объясняем каждый результат проверки комплекта

RustBeginner
Практиковаться сейчас

Введение

Надёжный список экземпляров — лишь начало проверки комплекта. Команде нужно объяснение каждого типа: сколько исправных предметов есть, какие повреждены, чего не хватает и что превышает план. Наличие незапланированного предмета или нулевой потребности не делает ввод недопустимым.

В этой работе вы реализуете логику сверки и прочитаете её отчёт для двух разных наборов фактических входных файлов. Среда независимая: парсер состояний и сборщик экземпляров из предыдущей работы уже предоставлены. Файлы из её VM не нужны. Предполагается знание её правил идентичности и конфликтов, а также структур, перечислений, HashMap, Result, чтения файлов и циклов Rust. Автор оценивает время в 45 минут с чтением и обычными исправлениями; это не измеренная на начинающих продолжительность.

Сверяем каждый тип и экземпляр

На этом шаге постройте сверку тремя правками: определите нужные типы, соберите полные множества их экземпляров, затем вычислите недостачу и излишек. Откройте Terminal. Добавьте Cargo в путь поиска этой оболочки и войдите в независимо подготовленный проект. Повторите экспорт в новом терминале, а cd — если потеряли местоположение:

export PATH=/home/labex/.cargo/bin:$PATH
cd /home/labex/project/field-kit

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

cat data/plan.tsv
cargo run --quiet -- assets data/scans.tsv

В таблице шесть физических ID, не семь сканирований. a, b и c — исправные экземпляры light-a. d — повреждённый light-b; e и f — исправный и повреждённый экземпляры незапланированного типа other. Отображаемое название Flashlight принадлежит двум разным запланированным ID типов. План также требует две верёвки без сканирований и ноль запасных предметов без сканирований. Сохраняйте различия, решая, что должен объяснять полный отчёт.

Меняйте только src/readiness.rs, сохраняя публичное имя функции и типы параметров и результата. Не меняйте другие исходники, Cargo.toml, rust-toolchain.toml и каждый исходный файл data/. Вы создадите отдельные экспериментальные входы в каталоге проекта. Командный интерфейс читает реально названные пути; пример не подставляется.

Контракт ограниченного TSV тот же: точные заголовки трёх полей type_id, name, required для планов и asset_id, type_id, condition для сканирований, с буквальными табуляциями между полями. ID начинаются со строчной буквы ASCII, далее строчные буквы, цифры или -. Названия плана — непустые печатные строки ASCII, включая пробелы. Количества — десятичные цифры со значением 0–999, ведущие нули разрешены. ID типов плана не повторяются, отображаемые названия могут. Состояния — строго good или damaged. LF/CRLF, отсутствие финального перевода строки и совершенно пустые последующие строки принимаются. Поля не обрезаются; строки только из пробельных символов не пусты, а одиночные возвраты каретки не являются окончаниями строк. Это синтетические текстовые записи, не физические осмотры.

Прочитайте предоставленные типы и незавершённую функцию:

cat src/lib.rs
cat src/readiness.rs

Need содержит ID типа плана, название и требуемое количество. Scan содержит ID экземпляра, ID типа, состояние и диагностическую строку. reconcile получает &[Need] и &[Scan], заимствованные срезы, которые нельзя менять. Формат экземпляров уже проверен и они собраны по идентичности, поэтому каждый физический ID встречается один раз. Возвращённый Vec<TypeSummary> должен включать объединение запланированных и отсканированных типов, полные множества ID исправных и повреждённых, требуемое количество, недостачу и излишек. Подготовленное форматирование добавляет отображаемые названия, сортирует строки и списки ID и печатает связь каждого экземпляра planned или unplanned.

Первая правка: сохраните нужные типы

Рассмотрите план без отсканированных экземпляров. Положительная потребность всё равно означает недостачу, нулевая — нулевую недостачу. Создайте отдельный вход без сканирований; не опустошайте защищённый оригинал. printf печатает строку формата, интерпретируя \t как табуляцию и \n как перевод строки. Одинарные кавычки сохраняют обратные косые черты для printf, а > пишет указанный экспериментальный файл. Его заголовок — единственная строка:

printf 'asset_id\ttype_id\tcondition\n' > empty-scans.tsv
cat empty-scans.tsv

До изменения кода предскажите, какие типы должен сохранить этот ввод: light-a, light-b, rope и zero с потребностями 2, 1, 2 и 0. В этом конкретном вводе нет типа только из сканирований. Но общая функция должна находить типы из обоих срезов, чтобы позднейшие настоящие сканирования могли добавить other.

Создайте точку сохранения только исходника. mktemp -d создаёт новый каталог, заменяя шесть X уникальным суффиксом; предыдущая копия не используется повторно. $() захватывает напечатанный путь в переменную сеанса. && выполняет cp -p только после успешного создания каталога. Копирование сохраняет метаданные исходника, кавычки переменной — путь целиком. Если любая операция не удалась, прочитайте ошибку и создайте копию до редактирования:

rs03_before_types=$(mktemp -d .edit-before-types.XXXXXX) && cp -p src/readiness.rs "$rs03_before_types/readiness.rs"

Откройте существующий файл в nano; -c показывает позицию курсора:

nano -c src/readiness.rs

Nano обозначает Ctrl символом ^. Нажмите Ctrl+W, найдите pub fn reconcile, затем нажмите Enter. Прочитайте открывающую и закрывающую скобки функции. Стрелками и Backspace/Delete меняйте только её тело и неиспользуемые имена параметров. Переименуйте _needs и _assets в needs и assets, сохраняя типы. Сохраните предоставленный импорт use crate::{Need, Scan, TypeSummary};; этот этап пока не использует Condition.

Постройте первый этап из отношений данных. Создайте пустой вектор types. Добавьте каждый ID запланированного типа. Затем посетите экземпляры и добавляйте ID типа только когда types.contains(&asset.type_id) ложно. План уже имеет уникальные ID типов. .clone() копирует принадлежащую значению строку ID типа из заимствованной записи, позволяя результату владеть ею.

Далее создайте пустой вектор summaries и посетите каждый собранный ID типа. Начните требуемое количество с нуля, затем найдите тип в плане. usize::from(need.required) преобразует значение плана u16 в тип счётчика, используемый длинами векторов. Пока используйте пустые векторы good_ids и damaged_ids. Пока пригодные экземпляры не посчитаны, присвойте missing значение required, а surplus — ноль. Добавьте TypeSummary с этими переменными, а summaries сделайте последним выражением функции. Сокращённая запись полей вроде type_id, required использует одноимённые переменные. Классификация экземпляров ещё не реализована.

Сохраните через Ctrl+O, Enter для подтверждения текущего имени, затем Ctrl+X для выхода. Прочитайте сохранённую функцию до запуска:

cat src/readiness.rs

cargo run компилирует и запускает программу; --quiet скрывает ход работы Cargo, -- передаёт последующие аргументы программе. Проверьте ограниченный случай без сканирований:

cargo run --quiet -- check data/plan.tsv empty-scans.tsv

Ожидайте четыре запланированных типа, ноль исправных экземпляров и пустые множества ID, недостачи 2, 1, 2 и 0 и нулевые излишки. После заголовка ASSET не должно быть строк данных экземпляров. Первый этап корректен только для этого наблюдения без сканирований. Это не полная реализация для непустого списка экземпляров. Не считайте нулевой статус завершения доказательством завершения всех правил сверки.

Вторая правка: сохраните полные множества состояний

Теперь предскажите полные множества для настоящих сканирований: у light-a исправные ID a,b,c; у light-b повреждённый ID d; у other исправный ID e и повреждённый ID f. rope и zero всё ещё требуют собственных сводок даже без экземпляров. Сохраните каждое множество вместо выбора предпочтительного «лишнего» экземпляра.

Сохраните работающий этап объединения типов:

rs03_before_sets=$(mktemp -d .edit-before-sets.XXXXXX) && cp -p src/readiness.rs "$rs03_before_sets/readiness.rs"
nano -c src/readiness.rs

Вверху измените импорт crate, включив теперь используемое перечисление:

use crate::{Condition, Need, Scan, TypeSummary};

Это явная правка исходника: исходная заготовка не импортирует Condition. crate обозначает эту библиотеку; use вводит её имена в этот модуль. Добавление импорта вместе с первым использованием также предотвращает предупреждение о неиспользуемом импорте на предыдущем этапе.

Найдите pub fn reconcile. Сохраните логику объединения типов и требуемого количества. Сделайте good_ids и damaged_ids изменяемыми для добавления ID. Внутри цикла каждого типа посетите все экземпляры, выберите имеющие текущий type_id и сопоставьте их состояние. Для варианта Good добавьте клон ID экземпляра в исправный вектор; для Damaged — в повреждённый. Оба варианта — допустимый ввод. Не удаляйте повреждённые или незапланированные экземпляры. Оставьте два временных присваивания недостачи и излишка без изменений до следующей правки.

Сохраните, выйдите и запустите с настоящими сканированиями:

cargo run --quiet -- check data/plan.tsv data/scans.tsv

Прочитайте ID типов, потребности, числа исправных, полные множества ID и строки ASSET. Они должны соответствовать предсказанию, включая other. Столбцы недостачи и излишка пока временные и не являются допустимыми общими результатами. Например, временная недостача light-a ещё может показывать 2, хотя три исправных ID уже присутствуют. Объясните, почему это не завершённый отчёт. Далее вы исправите два расчёта; не заявляйте, что промежуточная таблица проходит итоговую проверку.

Третья правка: вычислите недостачу и излишек только по исправным

Только исправные уникальные экземпляры покрывают потребность. Для light-a требование 2 и исправных 3 означают недостачу 0, излишек 1. Для light-b повреждённый экземпляр не покрывает потребность: недостача 1, излишек 0. У rope недостача 2. zero остаётся 0/0. У незапланированного other потребность ноль и один исправный экземпляр, поэтому излишек 1; повреждённый ID сохраняется, но не увеличивает излишек.

Сохраните этап, уже собирающий правильные множества:

rs03_before_totals=$(mktemp -d .edit-before-totals.XXXXXX) && cp -p src/readiness.rs "$rs03_before_totals/readiness.rs"
nano -c src/readiness.rs

Найдите pub fn reconcile, затем замените только временные выражения недостачи и излишка. len() вектора даёт число элементов. saturating_sub вычитает без ухода ниже нуля, предотвращая переполнение беззнакового числа при вычитании. Правила — missing = max(required - good, 0) и surplus = max(good - required, 0); применяйте их к good_ids.len(), никогда к числу исходных сканирований или повреждённых ID. Сохраните объединение типов и полные векторы. Сохраните через Ctrl+O, Enter, затем Ctrl+X.

cargo run --quiet -- check data/plan.tsv data/scans.tsv
TYPE	type_id	name	required	good	missing	surplus	good_ids	damaged_ids
TYPE	light-a	Flashlight	2	3	0	1	a,b,c	-
TYPE	light-b	Flashlight	1	0	1	0	-	d
TYPE	other	-	0	1	0	1	e	f
TYPE	rope	Rope	2	0	2	0	-	-
TYPE	zero	Spare	0	0	0	0	-	-
ASSET	asset_id	type_id	condition	plan
ASSET	a	light-a	good	planned
ASSET	b	light-a	good	planned
ASSET	c	light-a	good	planned
ASSET	d	light-b	damaged	planned
ASSET	e	other	good	unplanned
ASSET	f	other	damaged	unplanned

Теперь каждое количество прослеживается: light-b сохраняет повреждённый ID d, но имеет недостачу; other сохраняет оба ID, но только e создаёт излишек. Повторное сканирование a не создало ещё один физический экземпляр. Повторите прежнюю проверку пустого источника с завершённой функцией:

cargo run --quiet -- check data/plan.tsv empty-scans.tsv

Её четыре запланированных типа и недостачи без сканирований должны оставаться правильными. Только теперь ожидается прохождение полной проверки сверки этого шага. Эквивалентные внутренние порядки коллекций допустимы, так как подготовленное форматирование сортирует отображаемые строки и списки ID.

Исправьте исходник без сброса проекта

Ошибка Cargo называет файл и строку. Откройте файл, найдите функцию через Ctrl+W и исправьте указанную правку. Если случайное изменение ещё не сохранено, Ctrl+X, затем N при вопросе о сохранении отбрасывают его; это не отменяет прежнюю сохранённую правку.

Только при восстановлении после сохранённой ошибки выберите точку этапа, который хотите повторить: .edit-before-types.* содержит начальную заготовку, .edit-before-sets.* — этап объединения типов, .edit-before-totals.* — полные множества ID до итоговых расчётов. В новом Terminal повторите PATH и cd, затем перечислите настоящие имена копий:

ls -d .edit-before-*

-d перечисляет имена каталогов, не содержимое, * соответствует созданным суффиксам. Выполните следующую команду; пока оболочка ждёт, вставьте одно точное напечатанное имя каталога и нажмите Enter. read сохраняет выбор в переменной этого сеанса:

read rs03_restore

Подтвердите выбранный исходник до копирования:

cat "$rs03_restore/readiness.rs"

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

cp -p "$rs03_restore/readiness.rs" src/readiness.rs

После этого повторите затронутые правки и тесты; точка сохранения исходника не меняет ввод. Никогда не меняйте исходный план под неверный алгоритм. Эталон ниже предназначен для сравнения конкретной части после вашей попытки, не для замены всего файла по умолчанию.

Полный эталон сверки для сравнения и восстановления
use crate::{Condition, Need, Scan, TypeSummary};

pub fn reconcile(needs: &[Need], assets: &[Scan]) -> Vec<TypeSummary> {
    let mut types = Vec::new();
    for need in needs {
        types.push(need.type_id.clone());
    }
    for asset in assets {
        if !types.contains(&asset.type_id) {
            types.push(asset.type_id.clone());
        }
    }
    let mut summaries = Vec::new();
    for type_id in types {
        let mut required = 0;
        for need in needs {
            if need.type_id == type_id {
                required = usize::from(need.required);
            }
        }
        let mut good_ids = Vec::new();
        let mut damaged_ids = Vec::new();
        for asset in assets {
            if asset.type_id == type_id {
                match asset.condition {
                    Condition::Good => good_ids.push(asset.asset_id.clone()),
                    Condition::Damaged => damaged_ids.push(asset.asset_id.clone()),
                }
            }
        }
        let missing = required.saturating_sub(good_ids.len());
        let surplus = good_ids.len().saturating_sub(required);
        summaries.push(TypeSummary {
            type_id, required, good_ids, damaged_ids, missing, surplus,
        });
    }
    summaries
}

Объясняем частичное покрытие по другому источнику

На этом шаге вы используете другие входные пути для проверки частичной недостачи и незапланированного типа только с повреждёнными предметами. Продолжайте в Terminal, в /home/labex/project/field-kit. Сохраните все исходные файлы data/, создав вместо их изменения два отдельных пробных файла ниже.

Короткие блоки ввода ниже используют here-document. cat получает следующие строки как ввод, > пишет названный экспериментальный файл, а <<'TSV' начинает буквальный ввод до строки только с TSV. Разделитель в кавычках предотвращает раскрытие содержимого оболочкой. Копируйте обе строки разделителей и сохраняйте буквальные табуляции. Это небольшие тестовые входы, не замена модуля Rust. Название Water filter содержит пробел внутри поля; поля разделяются табуляцией.

cat > trial-plan.tsv <<'TSV'
type_id	name	required
lamp	Lamp	2
filter	Water filter	5
zero	Spare	0
TSV
cat > trial-scans.tsv <<'TSV'
asset_id	type_id	condition
l-one	lamp	good
l-two	lamp	damaged
f-one	filter	good
f-two	filter	good
u-one	unplanned	damaged
TSV

Перед запуском предскажите недостачу ламп, фильтров и сохранится ли незапланированный тип только с повреждёнными предметами.

Теперь укажите эти фактические файлы:

cargo run --quiet -- check trial-plan.tsv trial-scans.tsv
TYPE	type_id	name	required	good	missing	surplus	good_ids	damaged_ids
TYPE	filter	Water filter	5	2	3	0	f-one,f-two	-
TYPE	lamp	Lamp	2	1	1	0	l-one	l-two
TYPE	unplanned	-	0	0	0	0	-	u-one
TYPE	zero	Spare	0	0	0	0	-	-
ASSET	asset_id	type_id	condition	plan
ASSET	f-one	filter	good	planned
ASSET	f-two	filter	good	planned
ASSET	l-one	lamp	good	planned
ASSET	l-two	lamp	damaged	planned
ASSET	u-one	unplanned	damaged	unplanned

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

Попробуйте одно контролируемое изменение, не меняя ни оригиналы, ни первые пробные сканирования. cp -p создаёт копию с сохранением метаданных скопированного файла. Используйте это отдельное новое имя; если уже использовали его в прежней попытке, сохраните ту работу или выберите другое свободное имя и согласованно используйте в следующих командах.

cp -p trial-scans.tsv trial-scans-one-repair.tsv
nano -c trial-scans-one-repair.tsv

Нажмите Ctrl+W, найдите l-two и нажмите Enter. Только на этой строке замените состояние damaged на good, используя стрелки и Backspace/Delete. Сохраните ID экземпляра, ID типа, заголовок и буквальные разделители-табуляции. Сохраните через Ctrl+O, Enter, затем выйдите через Ctrl+X. Это намеренная правка отдельного эксперимента, не исправление защищённых свидетельств.

До запуска предскажите две затронутые строки отчёта: TYPE lamp теперь должна иметь два исправных ID, ни одного повреждённого, нулевую недостачу и излишек. ASSET l-two должна сменить состояние на good, оставаясь запланированной. Все другие строки типов и экземпляров должны быть прежними; потребность фильтров и повреждённый незапланированный предмет не менялись.

cargo run --quiet -- check trial-plan.tsv trial-scans-one-repair.tsv

Прочитайте две строки и убедитесь, что остальные всё ещё совпадают. Если изменилось больше, изучите копию через cat и исправьте непреднамеренное поле; не меняйте алгоритм под случайную правку ввода. Вернитесь к неизменённому первому пробному источнику:

cargo run --quiet -- check trial-plan.tsv trial-scans.tsv

Недостача ламп должна вернуться к одной, с повреждённым l-two. Отчёты отличаются из-за различия реально названных входов; ни один источник молча не заменяется. Та же завершённая реализация сверки должна объяснять оба случая.

Ошибка ввода не должна превращаться в отчёт о недостаче или незаметно возвращать исходный пример. Следующее неправильное имя файла намеренно и должно дать ненулевой статус:

cargo run --quiet -- check trial-plans.tsv trial-scans.tsv

Ошибка называет несуществующий trial-plans.tsv. Исправьте лишнее окончание множественного числа и повторите команду:

cargo run --quiet -- check trial-plan.tsv trial-scans.tsv

Для намеренной ошибки формата используйте существующий пример с неизвестным состоянием. Команда тоже должна вернуть ненулевой статус и назвать data/bad-status.tsv вместе с его строкой:

cargo run --quiet -- check trial-plan.tsv data/bad-status.tsv

broken — недопустимое слово, а damaged — допустимое состояние, которое не покрывает потребность. Для восстановления снова выберите корректные пробные сканирования:

cargo run --quiet -- check trial-plan.tsv trial-scans.tsv

Наконец вернитесь к исходным входным данным:

cargo run --quiet -- check data/plan.tsv data/scans.tsv

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

Резюме

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