Wir sollten nicht vergessen, dass das Wesentliche der Aufgabe in der Tat recht einfach ist; wie in einem Tutorial über Haskell (das sich um das Durcharbeiten der Lösung für diese Aufgabe dreht, schrittweise verfeinert)
Lassen Sie uns nun einen Moment darüber nachdenken, wie unser Programm funktionieren wird, und es im Pseudocode ausdrücken:
main = Read list of directories and their sizes.
Decide how to fit them on CD-Rs.
Print solution.
Klingt vernünftig? Ich dachte auch.
Lassen Sie uns unser Leben ein wenig vereinfachen und davon ausgehen, dass wir die Verzeichnisgrößen irgendwo außerhalb unseres Programms berechnen (zum Beispiel mit " du -sb *
") und diese Informationen von stdin lesen.
(vom Per Anhalter durch Haskell, Kapitel 1 )
(Außerdem möchten Sie in Ihrer Frage in der Lage sein, die resultierenden Festplattenlayouts zu optimieren (zu bearbeiten) und sie dann mit einem Tool zu brennen.)
Sie können eine einfache Variante des Programms aus diesem Haskell-Tutorial zum Aufteilen Ihrer Dateisammlung wiederverwenden (anpassen und wiederverwenden).
Leider wird in dem distribute
Tool, das ich hier in einer anderen Antwort erwähnt habe , die Einfachheit der wesentlichen Aufteilungsaufgabe nicht durch die Komplexität und Aufblähung der Benutzeroberfläche von erreicht distribute
(da es geschrieben wurde, um mehrere Aufgaben zu kombinieren, obwohl es schrittweise ausgeführt wird). aber immer noch nicht so sauber kombiniert, wie ich es mir jetzt vorstellen kann).
Um Ihnen zu helfen, den Code besser zu nutzen, finden Sie hier einen Auszug aus dem Bash-Code von distribute
(in Zeile 380 ), der dazu dient, diese "wesentliche" Aufgabe des Aufteilens einer Sammlung von Dateien auszuführen :
# 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() {
( Lesen Sie mehr nach Zeile 454 )
Beachten Sie, dass die eatFiles
Funktion die Layouts der zukünftigen Datenträger als Bäume vorbereitet, in denen die Blätter Symlinks zu den realen Dateien darstellen. Es entspricht also Ihrer Anforderung, dass Sie die Layouts vor dem Brennen bearbeiten können müssen. Das mkisofs
Dienstprogramm hat die Option, Symlinks zu folgen, die tatsächlich im Code meiner mkiso
Funktion verwendet werden .
Das vorgestellte Skript (das Sie natürlich nehmen und nach Ihren Wünschen umschreiben können!) Folgt der einfachsten Idee: Die Größe der Dateien (oder genauer gesagt der Pakete im Fall von distribute
) nur in der angegebenen Reihenfolge zu summieren , don Keine Umlagerungen vornehmen.
Der "Per Anhalter durch Haskell" nimmt das Optimierungsproblem ernst und schlägt Programmvarianten vor, die versuchen würden, die Dateien intelligent neu anzuordnen, damit sie besser auf Datenträger passen (und weniger Datenträger benötigen):
Genug Vorbereitungen schon. Lass uns ein paar CDs packen.
Wie Sie vielleicht bereits erkannt haben, ist unser Problem ein klassisches. Es wird als "Rucksackproblem" bezeichnet
( googeln Sie es , wenn Sie noch nicht wissen, was es ist. Es gibt mehr als 100000 Links).
Beginnen wir mit der gierigen Lösung ...
(Lesen Sie mehr in Kapitel 3 und weiter.)
Andere intelligente Werkzeuge
Mir wurde auch gesagt, dass Debian ein Tool verwendet, um seine Distributions-CDs zu distribute
erstellen , das intelligenter ist als meine schriftlichen Paketsammlungen: Die Ergebnisse sind besser, weil es sich um Abhängigkeiten zwischen Paketen kümmert und versuchen würde, die Sammlung von Paketen zu erstellen, die sich weiterentwickeln Die erste Festplatte, die unter Abhängigkeiten geschlossen wurde, dh, kein Paket von der ersten Festplatte sollte ein Paket von einer anderen Festplatte erfordern (oder zumindest sollte die Anzahl solcher Abhängigkeiten minimiert werden).