Мы не должны забывать, что суть задачи действительно довольно проста; как положено в учебнике по 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» более серьезно относится к проблеме оптимизации и предлагает варианты программ, которые попытались бы разумно перестроить файлы, чтобы они лучше подходили для дисков (и требовали меньше дисков):
Уже достаточно предварительных экзаменов. давайте возьмем несколько компакт-дисков.
Как вы, возможно, уже поняли, наша проблема классическая. Это называется «проблема с рюкзаком»
( если вы еще не знаете, что это такое, запустите Google . Там более 100000 ссылок).
давайте начнем с жадного решения ...
(подробнее читайте в Главе 3 и далее.)
Другие умные инструменты
Мне также сказали, что Debian использует инструмент для создания своих дистрибутивных компакт-дисков, который умнее, чем мои distribute
коллекции пакетов: его результаты более хороши, потому что он заботится о зависимостях между пакетами и пытается создать коллекцию пакетов, которая попадает на первый диск закрыт под зависимостями, то есть ни один пакет с 1-го диска не должен требовать пакет с другого диска (или, по крайней мере, я бы сказал, количество таких зависимостей должно быть минимизировано).