Não devemos esquecer que a essência da tarefa é realmente bastante simples; conforme apresentado em um tutorial sobre Haskell (que é escrito sobre o trabalho da solução para esta tarefa, aprimorado de forma incremental)
Agora, vamos pensar por um momento sobre como nosso programa funcionará e expressá-lo em pseudocódigo:
main = Read list of directories and their sizes.
Decide how to fit them on CD-Rs.
Print solution.
Parece razoável? Eu pensei assim.
Vamos simplificar um pouco a nossa vida e supor, por enquanto, que calcularemos o tamanho dos diretórios em algum lugar fora do nosso programa (por exemplo, com " du -sb *
") e leremos essas informações no stdin.
(do guia Mochileiros para Haskell, capítulo 1 )
(Além disso, na sua pergunta, você deseja ajustar (editar) os layouts de disco resultantes e usar uma ferramenta para gravá-los.)
Você pode reutilizar (adaptar e reutilizar) uma variante simples do programa desse tutorial Haskell para dividir sua coleção de arquivos.
Infelizmente, na distribute
ferramenta que eu mencionei aqui em outra resposta , a simplicidade da tarefa divisão essencial não é compensada pela complexidade e bloatedness da interface do usuário de distribute
(porque foi escrito para combinar várias tarefas, embora realizada em etapas, mas ainda assim combinada, não da maneira mais limpa que eu conseguia pensar agora).
Para ajudá-lo a usar seu código, aqui está um trecho do código de referência distribute
(na linha 380 ) que serve para executar esta tarefa "essencial" de dividir uma coleção de arquivos:
# 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() {
( leia mais após a linha 454 )
Observe que a eatFiles
função prepara os layouts dos futuros discos como árvores nas quais as folhas são links simbólicos para os arquivos reais. Portanto, é um requisito que você possa editar os layouts antes de gravar. O mkisofs
utilitário tem uma opção para seguir links simbólicos, que são de fato empregados no código da minha mkiso
função .
O script apresentado (que você pode pegar e reescrever para suas necessidades, é claro!) Segue a idéia mais simples: somar o tamanho dos arquivos (ou, mais precisamente, os pacotes no caso de distribute
) apenas na ordem em que foram listados, não faça qualquer rearranjo.
O "Guia do Hitchhikers para Haskell" leva o problema de otimização mais a sério e sugere variantes de programa que tentariam reorganizar os arquivos de maneira inteligente, para que se encaixem melhor em discos (e exijam menos discos):
Já chega de preliminares. vamos embalar alguns CDs.
Como você já deve ter reconhecido, nosso problema é clássico. É chamado de "problema da mochila"
( pesquise no Google , se você ainda não sabe o que é. Existem mais de 100000 links).
vamos começar com a solução gananciosa ...
(leia mais no capítulo 3 e mais).
Outras ferramentas inteligentes
Foi-me dito também que o Debian usa uma ferramenta para tornar seus CDs de distribuição mais inteligentes que minhas distribute
coleções de pacotes wrt: seus resultados são melhores porque se preocupam com as dependências entre pacotes e tentam fazer a coleção de pacotes o primeiro disco fechado sob dependências, ou seja, nenhum pacote do 1º disco deve exigir um pacote de outro disco (ou pelo menos, eu diria, o número dessas dependências deve ser minimizado).