Не слід забувати, що суть завдання справді досить проста; як укладено в підручнику з Haskell (який написано навколо опрацювання рішення для цього завдання, поступово уточнений)
Тепер давайте на хвилину подумаємо про те, як буде працювати наша програма, і висловимо її у псевдокоді:
main = Read list of directories and their sizes.
Decide how to fit them on CD-Rs.
Print solution.
Звучить розумно? Я так думав.
Давайте трохи спростимо наше життя і припустимо, що зараз ми будемо обчислювати розміри каталогів десь поза нашою програмою (наприклад, з " du -sb *
") та читати цю інформацію з stdin.
(з посібника з автостопом до Хаскелла, глава 1 )
(Крім того, у своєму запитанні ви хочете мати змогу налаштувати (відредагувати) отримані диски, а потім використати інструмент для їх запису.)
Ви можете повторно використовувати (адаптувати та повторно використовувати) простий варіант програми з цього підручника Haskell для розділення колекції файлів.
На жаль, в на distribute
інструменті , який я вже згадував тут , в іншому відповідь , простота найважливішого завдання поділу не відповідає складності та здуттям призначеного для користувача інтерфейсу distribute
(бо вона була написана , щоб об'єднати кілька завдань, хоча проводяться поетапно, але все-таки комбінований не найчистішим способом, про який я міг зараз подумати).
Щоб допомогти вам скористатись його кодом, ось уривок з bash-коду distribute
(у рядку 380 ), який виконує цю "найважливішу" задачу розділити колекцію файлів:
# Splitting:
function splitMirrorDir() {
if [[ ! -d "$THIS_BASES_DIR/$BASE/$type" ]]; then
echo $"No base fixed for $type" >&2
exit 1
fi
# Getting the list of all suitable files:
local -a allFiles
let 'no = 0' ||:
allFiles=()
# no points to the next free position in allFiles
# allFiles contains the constructed list
for p in "$THIS_BASES_DIR/$BASE/$type"/*.rpm; do
if [[ ! -e "$p" ]]; then
# fail on non-existent files
echo $"Package file doesn't exist: " "$p" >&2
return 1
fi
if [[ "$ONLY_REAL_FILES" == "yes" && ! -f "$p" ]]; then
continue
fi
if [[ "$DIFF_TO_BASE" ]]; then
older_copy="$DIFF_TO_BASE/$type/${p##*/}" # using shell param expansion instead of `basename' to speed up
if [[ -h "$older_copy" || -a "$older_copy" ]]; then
continue
fi
fi
allFiles[$(( no++ ))]="$p"
done
readonly -a allFiles
# Splitting the list of all files into future disks:
#
local -a filesToEat allSizes
let 'no = 0' ||:
filesToEat=()
allSizes=($(getSize "${allFiles[@]}"))
readonly -a allSizes
# allSizes contains the sizes corrsponding to allFiles
# filesToEat hold the constructed list of files to put on the current disk
# no points to the next free position in filesToEat
# totalSize should hold the sum of the sizes
# of the files already put into filesToEat;
# it is set and reset externally.
for p in "${allFiles[@]}"; do
if (( totalsize + ${allSizes[$(( no ))]} > CDVOLUME )); then
eatFiles "${filesToEat[@]}"
filesToEat=()
finishCD
startTypedCD
fi
let "totalsize += ${allSizes[$(( no ))]}" ||:
filesToEat[$(( no++ ))]="$p"
done
eatFiles "${filesToEat[@]}"
}
function eatFiles() {
#{ oldIFS="$IFS"; IFS=$'\n'; echo "$FUNCNAME: args: " "$*" | head >&2; IFS="$oldIFS"; }
zeroDelimited "$@" | xargs -0 --no-run-if-empty \
cp -s \
--target-dir="$THIS_LAYOUTS_DIR/cd$(( cdN ))/$PREFIX/$type$DOT_SUFFIX"/ \
--
}
function startTypedCD() {
# set -x
mkdir -p "$THIS_LAYOUTS_DIR/cd$(( cdN ))/$PREFIX/$type$DOT_SUFFIX"
start_action $" %s with %s" "$(( cdN ))" "$type"
# set +x
}
function finishCD() {
( читати далі після рядка 454 )
Зауважте, що eatFiles
функція готує макети майбутніх дисків як дерева, де листя є посиланнями на реальні файли. Отже, це відповідає вашій вимозі, щоб ви мали змогу редагувати макети перед записом. mkisofs
Утиліта має можливість слідувати символічним посиланнями, яка на самому ділі використовувана в коді моєї mkiso
функції .
Представлений сценарій (який, звичайно, ви можете взяти та переписати під свої потреби!) Відповідає найпростішій ідеї: підсумовувати розміри файлів (або, точніше, пакунків у випадку distribute
) у тому порядку, в якому вони були перераховані, не не будую ніяких перестановок.
"Посібник з автостопом до Haskell" серйозно ставиться до проблеми оптимізації та пропонує варіанти програм, які б спробувати перевпорядкувати файли на розумному рівні, щоб вони краще вміщувалися на дисках (та вимагали менше дисків):
Досить попередніх ліній. підемо спакувати кілька компакт-дисків.
Як ви вже могли визнати, наша проблема є класичною. Це називається "проблема з рубанням"
( гугл вгору , якщо ви вже не знаєте, що це таке. Є більше 100000 посилань).
почнемо з жадного рішення ...
(детальніше читайте у розділі 3 та далі.)
Інші розумні інструменти
Мені сказали також, що Debian використовує інструмент для створення своїх дискотек на дистрибутиві, які розумніші за мої distribute
колекції пакетів wrt: його результати є кращими, оскільки він піклується про залежності міжпакетних пакетів і намагається зробити колекцію пакетів, які потрапляють на перший диск закритий під залежність, тобто жоден пакет з 1-го диска не повинен вимагати пакету з іншого диска (або, принаймні, я б сказав, кількість таких залежностей слід мінімізувати).