Zwei Linked Lists vergleichen

Anfängerfragen zum Programmieren mit PureBasic.
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7028
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Zwei Linked Lists vergleichen

Beitrag von STARGÅTE »

Ich habe festgestellt, dass Keys von Elementen die besonders lang sind (also ganze "Texte") auch zimlich lange brauchen wenn man mit ihnen arbeitet (also deren Hash zu errechnen).
Da es aber immer nur genau ein mal pro Element gemacht werden muss und eben keine Suche stattfinden muss, sollte es ab wenigen Elementen schneller sein.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Demivec
Beiträge: 49
Registriert: 22.02.2008 20:49
Wohnort: Utah, USA

Re: Zwei Linked Lists vergleichen

Beitrag von Demivec »

Mit Map:

Code: Alles auswählen

Procedure.i CompareLinkedLists(List source.DirectoryEntryDescriptor(), SourceRootPath.s, List Dest.DirectoryEntryDescriptor(), DestinationRootPath.s, List result.DirectoryEntryDescriptor())
  Shared OptionIncSysfiles
  Shared OptionIncHidfiles
  Define Size.f
  If ListSize(source()) = 0
    ProcedureReturn -1
  EndIf
  ClearList(result())
  Size = ListSize(source()) / 100
  If ListSize(Dest()) = 0
    CopyList(source(), result())
    ProcedureReturn ListSize(result())
  EndIf
  If Right(SourceRootPath, 1) <> "\"
    SourceRootPath + "\"
  EndIf
  If Right(DestinationRootPath, 1) <> "\"
    DestinationRootPath + "\"
  EndIf
  
  Protected NewMap comp.DirectoryEntryDescriptor()
  ForEach Dest()
    AddMapElement(comp(), Dest()\relativepath + Dest()\name)
  Next
  
  ForEach source()
    If (source()\issystem And OptionIncSysfiles = #False) Or (source()\ishidden And OptionIncHidfiles = #False)
      Continue
    EndIf

    If Not FindMapElement(comp(), source()\relativepath + source()\name)
      AddElement(result())
      result() = source()
    EndIf 
    
    If IsGadget(#mainProgressbar)
      SetGadgetState(#mainProgressbar, Round(ListIndex(source()) / Size, #PB_Round_Nearest))
    EndIf
  Next
  
  ProcedureReturn ListSize(result())
EndProcedure
Bild
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8808
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 64 GB DDR4-3200
Ubuntu 24.04.2 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken

Re: Zwei Linked Lists vergleichen

Beitrag von NicTheQuick »

Bei meiner Version dauert am längsten das Sortieren der beiden LinkedLists nach Dateiname. Danach ist die Laufzeit eigentlich das Maximum der beiden Listenlängen.
Aber da das Sortieren in O(log(n) * n) gehen sollte, also Quicksort, ist es wohl die schnellste Variante. Pro Element aus 'Dest' einen Hash zu bilden und diesen dann in der Tabelle zu suchen, dauert wahrscheinlich länger.
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7028
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Zwei Linked Lists vergleichen

Beitrag von STARGÅTE »

@NicTheQuick: Hä?
Aber Maps zeichen sich doch gerade dadurch aus, dass der Hash mehr oder weniger gleich der Index der Information ist.
Es findet also garkeine Suche statt. Klar kommt es zu Staus, wenn der Index mehrfach belegt ist, aber da man ja die Slots festlegen kann, kann man hier ruhig mal etwas mehr reservieren.
Das deine Version nach dem Sortieren schneller ist, ist klar, weil es ja dann nahezu Linear ist.

@Demivec:
Hier nimmst du ja die Map nur als zwischen Weg.
Ich hätte es gleich nur mit Maps gemacht und die Maps durchlaufen und elemente kopieren
wenn sie nicht in der anderen sind.
Denn vermutlich ich ja die Reihenfolge der Einträge eh egal oder?

Sry, aber ich habe gerade auch keine Lust es selber auszuprubieren^^
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8808
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 64 GB DDR4-3200
Ubuntu 24.04.2 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken

Re: Zwei Linked Lists vergleichen

Beitrag von NicTheQuick »

STARGÅTE hat geschrieben:@NicTheQuick: Hä?
Aber Maps zeichen sich doch gerade dadurch aus, dass der Hash mehr oder weniger gleich der Index der Information ist.
Es findet also garkeine Suche statt. Klar kommt es zu Staus, wenn der Index mehrfach belegt ist, aber da man ja die Slots festlegen kann, kann man hier ruhig mal etwas mehr reservieren.
Das deine Version nach dem Sortieren schneller ist, ist klar, weil es ja dann nahezu Linear ist.
Ja, das suchen eines Hashs dauert wohl nicht die Welt. Klar ist es eine Art Index, aber man muss trotzdem noch in logarithmischer Zeit aus dem Hash das richtige Element suchen.

Angenommen zwei Hashs sind gespeichert:

Code: Alles auswählen

FA34
FA43
Der interne Baum müsste dann so aussehen:

Code: Alles auswählen

[F] -> [A] -> [3] -> [4]
              [4] -> [3]
Ab [A] teilt sich also der interne Hashbaum. Jedesmal muss dann um einen Hash zu finden dieser Baum durchlaufen werden.
Benutzeravatar
Sicro
Beiträge: 963
Registriert: 11.08.2005 19:08
Kontaktdaten:

Re: Zwei Linked Lists vergleichen

Beitrag von Sicro »

Hallo Kiffi,

die Datenbank könntest du schneller füllen lassen, wenn du die INSERT-Befehle in einer Transaktion ausführen lässt. Standardmäßig wird jeder INSERT-Bfehl in einer eigenen Transaktion ausgeführt und das ist bei sehr vielen INSERT-Befehlen extrem langsam. Mit dem Befehl ROLLBACK kann auch die gesamte Transaktion (also alle hinzugefügten Einträge innerhalb der Transaktion) auf einen Schlag rückgängig gemacht werden. Weiteres kann hier nachgelesen werden: http://www.sqlite.org/lang_transaction.html

Weiß allerdings nicht, ob das mit der PB-internen SQLite-Engine geht.
Kiffi hat geschrieben:

Code: Alles auswählen

ForEach SourceFile()
  DatabaseUpdate(DB, "Insert Into SourceFiles (Path, Name) Values ('" + SourceFile()\Path + "', '" + SourceFile()\Name + "')")
Next

ForEach DestinationFile()
  DatabaseUpdate(DB, "Insert Into DestinationFiles (Path, Name) Values ('" + DestinationFile()\Path + "', '" + DestinationFile()\Name + "')")
Next
ändern zu

Code: Alles auswählen

DatabaseUpdate(DB, "BEGIN") ; Transaktion einleiten

ForEach SourceFile()
  DatabaseUpdate(DB, "Insert Into SourceFiles (Path, Name) Values ('" + SourceFile()\Path + "', '" + SourceFile()\Name + "')")
Next

ForEach DestinationFile()
  DatabaseUpdate(DB, "Insert Into DestinationFiles (Path, Name) Values ('" + DestinationFile()\Path + "', '" + DestinationFile()\Name + "')")
Next

DatabaseUpdate(DB, "COMMIT") ; Transaktion ausführen
Bild
Warum OpenSource eine Lizenz haben sollte :: PB-CodeArchiv-Rebirth :: Pleasant-Dark (Syntax-Farbschema) :: RegEx-Engine (kompiliert RegExes zu NFA/DFA)
Manjaro Xfce x64 (Hauptsystem) :: Windows 10 Home (VirtualBox) :: Neueste PureBasic-Version
Benutzeravatar
Kiffi
Beiträge: 10711
Registriert: 08.09.2004 08:21
Wohnort: Amphibios 9

Re: Zwei Linked Lists vergleichen

Beitrag von Kiffi »

@STARGÅTE & PMV: Asche auf mein Haupt! Dann habe ich das
wohl mit anderen Programmiersprachen verwechselt. In .Net
beispielsweise wird gewarnt, wenn man aus einem Thread heraus
eine GUI manipulieren will (kann man dennoch mit einem Flag
'CheckForIllegalCrossThreadCalls' umgehen).

Umso besser, wenn es in PB geht so problemlos geht. <)

@Sicro: Das mit den Transaktionen ist mir bekannt. Allerdings ist
der Performance-Gewinn bei :memory: - Datenbanken eher zu
vernachlässigen (bei einem kleinen Test 0.2 Sekunden bei 2 Millionen
Inserts), weil ja ohnehin alle DB-Operationen im Speicher stattfinden.
Aus diesem Grund habe ich Begin/Commit in meinem Beispiel weggelassen.

Aber grundsätzlich hast Du da schon recht.

Grüße ... Kiffi
a²+b²=mc²
Benutzeravatar
Shardik
Beiträge: 746
Registriert: 25.01.2005 12:19

Re: Zwei Linked Lists vergleichen

Beitrag von Shardik »

NicTheQuick hat geschrieben:PB-Strings sind immer Nullterminiert.
Das stimmt nicht! Es ist auch möglich, Strings mit fester Länge zu definieren,
sogenannte Fixed Strings, die nicht Nullterminiert sind:

Code: Alles auswählen

Define FixedString.S{10} ; String mit festgelegter Länge von 10 Zeichen
Benutzeravatar
Makke
Beiträge: 156
Registriert: 24.08.2011 18:00
Computerausstattung: AMD Ryzen 7 5700X - AMD Radeon RX 6800 XT - 32 GB DDR4 SDRAM
Wohnort: Ruhrpott
Kontaktdaten:

Re: Zwei Linked Lists vergleichen

Beitrag von Makke »

Vielen Dank nochmals an alle für die tolle Unterstützung hier. Ich habe den Code jetzt modifiziert, allerdings nicht alle Tips eingebaut. Für meine Bedürfnisse ist das ganze so ausreichend und schnell genug.

Weiterhin habe ich die Anpassung für Linux eingebaut (Vielen Dank an NickTheQuick) und bei mir in der VBox unter Ubuntuu 11.04 läuft das ebenfalls.

Und hier das Endprodukt:

Code: Alles auswählen

; Synchronizer
; synch two directories including their subdirs
; (c) 2011 by Makke and the members of pureboard.de
EnableExplicit
;- Constants
;-- App
#APPNAME = "SyncIt"
#APPMAJOR = 0
#APPMINOR = 2
;-- Window
Enumeration
  #mainWnd
  #toolWnd
EndEnumeration
;-- Gadgets
Enumeration
  #mainStatusbar
  #mainProgressbar
  #mainFrame_Source
  #mainFrame_Dest
  #mainFrame_Opt
  #mainFrame_Sync
  #mainBtn_DirSource
  #mainBtn_DirDest
  #mainBtn_Check
  #mainBtn_ShowDiff
  #mainBtn_Sync
  #mainChk_incDirs
  #mainChk_incSubdirs
  #mainChk_incSystem
  #mainChk_incHidden
  #mainTxt_SourceDir
  #mainTxt_SourceInfo
  #mainTxt_SourceFiles
  #mainTxt_SourceFilesNo
  #mainTxt_SourceDirs
  #mainTxt_SourceDirsNo
  #mainTxt_DestDir
  #mainTxt_DestInfo
  #mainTxt_DestFiles
  #mainTxt_DestFilesNo
  #mainTxt_DestDirs
  #mainTxt_DestDirsNo
  #mainTxt_Sync
  #toolLst_Diff
EndEnumeration
;-- OS dependent
CompilerSelect #PB_Compiler_OS 
  CompilerCase #PB_OS_Windows
    #Slash = "\"
  CompilerCase #PB_OS_Linux
    #Slash = "/"
  CompilerDefault ; don't know much about other OSes !
    End
CompilerEndSelect

;- Structures
Structure DirectoryEntryDescriptor
  path.s
  relativepath.s
  name.s
  size.i
  type.s
  isdir.b
  issystem.b
  ishidden.b
EndStructure

;- Structured Lists
NewList SourceFileList.DirectoryEntryDescriptor()
NewList DestFileList.DirectoryEntryDescriptor()
NewList DiffFileList.DirectoryEntryDescriptor()

;- Variables
;-- Windows
Define WindowEvent.i
Define WIndowID.i
Define GadgetID.i
Define QUIT.b
;-- Settings
Define SourceDir.s
Define DestDir.s
Define OptionIncDirs.i
Define OptionIncSubdirs.i
Define OptionIncSysfiles.i
Define OptionIncHidfiles.i
;-- Threads
Define SourceThreadID.i
Define DestThreadID.i
Define CheckForThread.i

;- Procedures
;-- Declarations
Declare.i GetDirectoryContent(List FileList.DirectoryEntryDescriptor(), FullPath.s, IncludeSubdirs.b = #False, AddDirEntryToList.b = #False)
Declare.i CountFilesInList(List ListOfFiles.DirectoryEntryDescriptor())
Declare.i CountDirsInList(List ListOfDirs.DirectoryEntryDescriptor())
Declare.i CompareLinkedLists(List Source.DirectoryEntryDescriptor(), SourceRootPath.s, List Dest.DirectoryEntryDescriptor(), DestinationRootPath.s, List Result.DirectoryEntryDescriptor())
Declare SelectOptions()
;-- Helper
Procedure.s StrB(BoolVariable.b)
  If BoolVariable = #True
    ProcedureReturn "True"
  Else
    ProcedureReturn "False"
  EndIf
EndProcedure
Procedure.i ValB(BoolExpression.s)
  If UCase(BoolExpression) = "TRUE"
    ProcedureReturn #True
  Else
    ProcedureReturn #False
  EndIf
EndProcedure
Procedure FinishList(List FileList.DirectoryEntryDescriptor(), FileListRootpath.s)
  If Right(FileListRootpath, 1) <> #Slash
    FileListRootpath = FileListRootpath + #Slash
  EndIf
  ForEach FileList()
    With FileList()
      \relativepath = ReplaceString(\path, FileListRootpath, "")
    EndWith
  Next
EndProcedure
;-- Settings
Procedure LoadSettings()
  Shared SourceDir
  Shared DestDir
  Shared OptionIncDirs
  Shared OptionIncSubdirs
  Shared OptionIncSysfiles
  Shared OptionIncHidfiles
  If OpenPreferences(#APPNAME + ".conf")
    SourceDir         = ReadPreferenceString("SourceDirectory", "")
    DestDir           = ReadPreferenceString("DestinationDirectory", "")
    OptionIncDirs     = ValB(ReadPreferenceString("IncludeDirectories", "True"))
    OptionIncSubdirs  = ValB(ReadPreferenceString("IncludeSubdirectories", "False"))
    OptionIncSysfiles = ValB(ReadPreferenceString("IncludeSystemfiles", "False"))
    OptionIncHidfiles = ValB(ReadPreferenceString("IncludeHiddenfiles", "False"))
    ClosePreferences()
  EndIf
EndProcedure
Procedure SaveSettings()
  Shared SourceDir
  Shared DestDir
  Shared OptionIncDirs
  Shared OptionIncSubdirs
  Shared OptionIncSysfiles
  Shared OptionIncHidfiles
  If CreatePreferences(#APPNAME + ".conf")
    PreferenceComment(#APPNAME + " v" + Str(#APPMAJOR) + "." + Str(#APPMINOR) + "." + Str(#PB_Editor_BuildCount) + " config file")
    WritePreferenceString("SourceDirectory", SourceDir)
    WritePreferenceString("DestinationDirectory", DestDir)
    WritePreferenceString("IncludeDirectories", StrB(OptionIncDirs))
    WritePreferenceString("IncludeSubdirectories", StrB(OptionIncSubdirs))
    WritePreferenceString("IncludeSystemfiles", StrB(OptionIncSysfiles))
    WritePreferenceString("IncludeHiddenfiles", StrB(OptionIncHidfiles))
    ClosePreferences()
  EndIf
EndProcedure
Procedure.i OpenSourceDir(CheckOnStartup.i=#False)
  Shared SourceFileList()
  Shared SourceDir
  Shared OptionIncDirs
  Shared OptionIncSubdirs
  Define time.i
  If SourceDir = ""
    ProcedureReturn #False
  EndIf
  If ListSize(SourceFileList()) > 0
    ClearList(SourceFileList())
  EndIf
  SetGadgetText(#mainTxt_SourceDir, SourceDir)
  If Not CheckOnStartup
    StatusBarText(#mainStatusbar, 0, "Scanning source directory ...")
    time = ElapsedMilliseconds()
  Else
    StatusBarText(#mainStatusbar, 0, "Scanning directories ...")
  EndIf
  GetDirectoryContent(SourceFileList(), SourceDir, OptionIncSubdirs, OptionIncDirs)
  If Not CheckOnStartup
    SetGadgetState(#mainProgressbar, 50)
  EndIf
  SetGadgetText(#mainTxt_SourceFilesNo, Str(CountFilesInList(SourceFileList())))
  SetGadgetText(#mainTxt_SourceDirsNo, Str(CountDirsInList(SourceFileList())))
  FinishList(SourceFileList(), SourceDir)
  If Not CheckOnStartup
    time = ElapsedMilliseconds() - time
    SetGadgetState(#mainProgressbar, 100)
    StatusBarText(#mainStatusbar, 0, "Done, took " + Str(time) + " miliseconds.")
  EndIf
  ProcedureReturn #True
EndProcedure
Procedure.i OpenDestDir(CheckOnStartup.i=#False)
  Shared DestFileList()
  Shared DestDir
  Shared OptionIncDirs
  Shared OptionIncSubdirs
  Define time.i
  If DestDir = ""
    ProcedureReturn #False
  EndIf
  If ListSize(DestFileList())
    ClearList(DestFileList())
  EndIf
  SetGadgetText(#mainTxt_DestDir, DestDir)
  If Not CheckOnStartup
    StatusBarText(#mainStatusbar, 0, "Scanning destination directory ...")
    time = ElapsedMilliseconds()
  Else
    StatusBarText(#mainStatusbar, 0, "Scanning directories ...")
  EndIf
  GetDirectoryContent(DestFileList(), DestDir, OptionIncSubdirs, OptionIncDirs)
  If Not CheckOnStartup
    SetGadgetState(#mainProgressbar, 50)
  EndIf
  SetGadgetText(#mainTxt_DestFilesNo, Str(CountFilesInList(DestFileList())))
  SetGadgetText(#mainTxt_DestDirsNo, Str(CountDirsInList(DestFileList())))
  FinishList(DestFileList(), DestDir)
  If Not CheckOnStartup
    time = ElapsedMilliseconds() - time
    SetGadgetState(#mainProgressbar, 100)
    StatusBarText(#mainStatusbar, 0, "Done, took " + Str(time) + " miliseconds.")
  EndIf
  ProcedureReturn #True
EndProcedure
;-- Directory
Procedure.i GetDirectoryContent(List FileList.DirectoryEntryDescriptor(), FullPath.s, IncludeSubdirs.b = #False, AddDirEntryToList.b = #False)
  Define dirId.i
  Define entrytype.i
  If Right(FullPath, 1) <> #Slash
    FullPath = FullPath + #Slash
  EndIf
  dirId = ExamineDirectory(#PB_Any, FullPath, "*.*")
  If IsDirectory(dirId)
    While NextDirectoryEntry(dirId)
      entrytype = DirectoryEntryType(dirID)
      If entrytype = #PB_DirectoryEntry_File
        AddElement(FileList())
        With FileList()
          \path  = FullPath
          \name  = DirectoryEntryName(dirId)
          \size  = DirectoryEntrySize(dirId)
          \type  = GetExtensionPart(\name)
          \isdir = #False
          CompilerSelect #PB_Compiler_OS
            CompilerCase #PB_OS_Windows
              If DirectoryEntryAttributes(dirId) & #PB_FileSystem_System
                \issystem = #True
              EndIf
              If DirectoryEntryAttributes(dirId) & #PB_FileSystem_Hidden
                \ishidden = #True
              EndIf
            CompilerCase #PB_OS_Linux
              If Left(\name, 1) = "."
                \ishidden = #True
              EndIf
          CompilerEndSelect
        EndWith
      ElseIf entrytype = #PB_DirectoryEntry_Directory
        If AddDirEntryToList
          If DirectoryEntryName(dirId) <> "." And DirectoryEntryName(dirId) <> ".."
            AddElement(FileList())
            With FileList()
              \path  = FullPath
              \name  = DirectoryEntryName(dirId)
              \size  = 0
              \type  = "DIR"
              \isdir = #True
              CompilerSelect #PB_Compiler_OS
                CompilerCase #PB_OS_Windows
                  If DirectoryEntryAttributes(dirId) & #PB_FileSystem_System
                    \issystem = #True
                  EndIf
                  If DirectoryEntryAttributes(dirId) & #PB_FileSystem_Hidden
                    \ishidden = #True
                  EndIf
                CompilerCase #PB_OS_Linux
                  If Left(\name, 1) = "."
                    \ishidden = #True
                  EndIf
              CompilerEndSelect
            EndWith
          EndIf
        EndIf
        If IncludeSubdirs
          If DirectoryEntryName(dirId) <> "." And DirectoryEntryName(dirId) <> ".."
            GetDirectoryContent(FileList(), FullPath + DirectoryEntryName(dirId) + "\", #True, AddDirEntryToList)
          EndIf
        EndIf
      EndIf
    Wend
    FinishDirectory(dirID)
  EndIf
  ProcedureReturn ListSize(FileList())
EndProcedure
Procedure.i CompareLinkedLists(List Source.DirectoryEntryDescriptor(), SourceRootPath.s, List Dest.DirectoryEntryDescriptor(), DestinationRootPath.s, List Result.DirectoryEntryDescriptor())
  Shared OptionIncSysfiles
  Shared OptionIncHidfiles
  Define FoundEntry.b = #False
  Define size.f
  If ListSize(Source()) = 0
    ProcedureReturn -1
  EndIf
  If ListSize(Result()) > 0
    ClearList(Result())
  EndIf
  size = ListSize(Source()) / 100
  If ListSize(Dest()) = 0
    CopyList(Source(), Result())
    ProcedureReturn ListSize(Result())
  EndIf
  If Right(SourceRootPath, 1) <> #Slash
    SourceRootPath = SourceRootPath + #Slash
  EndIf
  If Right(DestinationRootPath, 1) <> #Slash
    DestinationRootPath = DestinationRootPath + #Slash
  EndIf
  ForEach Source()
    If Source()\issystem And OptionIncSysfiles = #False
      Continue
    EndIf
    If Source()\ishidden And OptionIncHidfiles = #False
      Continue
    EndIf
    ForEach Dest()
      If Source()\relativepath = Dest()\relativepath
        If Source()\name = Dest()\name
          DeleteElement(Dest())
          FoundEntry = #True
          Break
        EndIf
      EndIf
    Next
    If FoundEntry = #False
      AddElement(Result())
      Result() = Source()
    Else
      FoundEntry = #False
    EndIf
    If IsGadget(#mainProgressbar)
      SetGadgetState(#mainProgressbar, Round(ListIndex(Source()) / size, #PB_Round_Nearest))
    EndIf
  Next
  ProcedureReturn ListSize(Result())
EndProcedure
Procedure.i CountFilesInList(List ListOfFiles.DirectoryEntryDescriptor())
  Define NoOfFiles.i
  If ListSize(ListOfFiles()) = 0
    ProcedureReturn 0
  EndIf
  ForEach ListOfFiles()
    If ListOfFiles()\isdir = #False
      NoOfFiles = NoOfFiles + 1
    EndIf
  Next
  ProcedureReturn NoOfFiles
EndProcedure
Procedure.i CountDirsInList(List ListOfDirs.DirectoryEntryDescriptor())
  Define NoOfDirs.i
  If ListSize(ListOfDirs()) = 0
    ProcedureReturn 0
  EndIf
  ForEach ListOfDirs()
    If ListOfDirs()\isdir = #True
      NoOfDirs = NoOfDirs + 1
    EndIf
  Next
  ProcedureReturn NoOfDirs
EndProcedure
Procedure.i CountAllEntriesInList(List ListToCount.DirectoryEntryDescriptor())
  ProcedureReturn ListSize(ListToCount())
EndProcedure
Procedure CheckDestinationPath(SourcePath.s)
  Shared SourceDir
  Shared DestDir
  Define pathpart.s = ReplaceString(SourcePath, SourceDir, "")
  Define subdirs.i  = CountString(pathpart, "\")
  Define i.i
  Define buildpath.s
  Define dir.i
  If Right(DestDir, 1) <> #Slash
    DestDir = DestDir + #Slash
  EndIf
  buildpath = DestDir
  For i = 1 To subdirs
    buildpath = buildpath + StringField(pathpart, i, "\") + #Slash
    dir = ExamineDirectory(#PB_Any, buildpath, "*")
    If IsDirectory(dir)
      FinishDirectory(dir)
    Else
      CreateDirectory(buildpath)
    EndIf
  Next
EndProcedure
;-- Window
Procedure OpenMainWindow()
  Define FontID1.i
  Define FontID2.i
  ; create window
  If OpenWindow(#mainWnd, 438, 258, 600, 300, #APPNAME,  #PB_Window_SystemMenu | #PB_Window_TitleBar | #PB_Window_MinimizeGadget | #PB_Window_Invisible)
    ; load fonts and set windows default font
    FontID1 = LoadFont(1, "Segoe UI", 8)
    FontID2 = LoadFont(2, "Segoe UI", 10)
    SetGadgetFont(#PB_Default, FontID1)
    ; create statusbar
    If CreateStatusBar(#mainStatusbar, WindowID(#mainWnd))
      AddStatusBarField(400)
      AddStatusBarField(200)
      StatusBarText(#mainStatusbar, 0, "Select a source and destination directory ...")
      StatusBarText(#mainStatusbar, 1, "")
    EndIf
    ; create gadgets
    ProgressBarGadget(#mainProgressbar, 405, 282, 190, 15, 0, 100, #PB_ProgressBar_Smooth)
    ; source frame content
    Frame3DGadget(#mainFrame_Source, 5, 10, 290, 125, "Source Directory:") : SetGadgetFont(#mainFrame_Source, FontID2)
    TextGadget(#mainTxt_SourceDir, 10, 30, 260, 20, "", #PB_Text_Border)
    ButtonGadget(#mainBtn_DirSource, 270, 30, 20, 20, "...")
    TextGadget(#mainTxt_SourceInfo, 10, 55, 280, 20, "Information:")
    TextGadget(#mainTxt_SourceFiles, 10, 80, 100, 20, "No. of Files:")
    TextGadget(#mainTxt_SourceFilesNo, 115, 80, 175, 20, "")
    TextGadget(#mainTxt_SourceDirs, 10, 105, 100, 20, "No. of Directories:")
    TextGadget(#mainTxt_SourceDirsNo, 115, 105, 175, 20, "")
    ; destination frame content
    Frame3DGadget(#mainFrame_Dest, 305, 10, 290, 125, "Destination Directory:") : SetGadgetFont(#mainFrame_Dest, FontID2)
    TextGadget(#mainTxt_DestDir, 310, 30, 260, 20, "", #PB_Text_Border)
    ButtonGadget(#mainBtn_DirDest, 570, 30, 20, 20, "...")
    TextGadget(#mainTxt_DestInfo, 310, 55, 280, 20, "Information:")
    TextGadget(#mainTxt_DestFiles, 310, 80, 100, 20, "No. of Files:")
    TextGadget(#mainTxt_DestFilesNo, 415, 80, 175, 20, "")
    TextGadget(#mainTxt_DestDirs, 310, 105, 100, 20, "No. of Directories:")
    TextGadget(#mainTxt_DestDirsNo, 415, 105, 175, 20, "")
    ; filter frame content
    Frame3DGadget(#mainFrame_Opt, 5, 145, 590, 55, "Filter Options:") : SetGadgetFont(#mainFrame_Opt, FontID2)
    CheckBoxGadget(#mainChk_incDirs, 20, 170, 135, 20, "include directories")
    CheckBoxGadget(#mainChk_incSubdirs, 160, 170, 135, 20, "include sub directories")
    CompilerSelect #PB_Compiler_OS
      CompilerCase #PB_OS_Windows
        CheckBoxGadget(#mainChk_incSystem, 310, 170, 135, 20, "include system files")
        CheckBoxGadget(#mainChk_incHidden, 455, 170, 135, 20, "include hidden files")
      CompilerCase #PB_OS_Linux
        CheckBoxGadget(#mainChk_incHidden, 310, 170, 135, 20, "include hidden files")
    CompilerEndSelect
    ; sync frame content
    Frame3DGadget(#mainFrame_Sync, 5, 210, 590, 60, "Synchronize:") : SetGadgetFont(#mainFrame_Sync, FontID2)
    ButtonGadget(#mainBtn_Check, 10, 235, 125, 20, "Check Directories") : DisableGadget(#mainBtn_Check, #True)
    TextGadget(#mainTxt_Sync, 140, 225, 320, 20, "", #PB_Text_Center)
    ButtonGadget(#mainBtn_ShowDiff, 238, 245, 125, 20, "Show Differences") : DisableGadget(#mainBtn_ShowDiff, #True)
    ButtonGadget(#mainBtn_Sync, 465, 235, 125, 20, "Copy files") : DisableGadget(#mainBtn_Sync, #True)
    ; set checkboxes
    SelectOptions()
    ; show window
    HideWindow(#mainWnd, #False)
  EndIf
EndProcedure
Procedure OpenDifferenceWindow()
  If OpenWindow(#toolWnd, WindowX(#mainWnd), WindowY(#mainWnd) + 40, WindowWidth(#mainWnd), 200, "", #PB_Window_Tool | #PB_Window_SystemMenu, WindowID(#mainWnd))
    ListViewGadget(#toolLst_Diff, 0, 0, WindowWidth(#toolWnd), WindowHeight(#toolWnd))
  EndIf
EndProcedure
;-- Gadget Events
Procedure SelectOptions()
  Shared OptionIncDirs
  Shared OptionIncSubdirs
  Shared OptionIncSysfiles
  Shared OptionIncHidfiles
  If OptionIncDirs
    SetGadgetState(#mainChk_incDirs, #True)
  EndIf
  If OptionIncSubdirs
    OptionIncDirs = #True
    SetGadgetState(#mainChk_incDirs, #True)
    SetGadgetState(#mainChk_incSubdirs, #True)
  EndIf
  CompilerSelect #PB_Compiler_OS
    CompilerCase #PB_OS_Windows
      If OptionIncSysfiles
        SetGadgetState(#mainChk_incSystem, #True)
      EndIf
      If OptionIncHidfiles
        SetGadgetState(#mainChk_incHidden, #True)
      EndIf
    CompilerCase #PB_OS_Linux
      If OptionIncHidfiles
        SetGadgetState(#mainChk_incHidden, #True)
      EndIf
  CompilerEndSelect
EndProcedure
Procedure SelectSourceDir()
  Shared SourceFileList()
  Shared SourceDir
  Shared OptionIncDirs
  Shared OptionIncSubdirs
  Define time.i
  SourceDir = PathRequester("Select source directory", ".")
  If SourceDir <> ""
    If ListSize(SourceFileList()) > 0
      ClearList(SourceFileList())
    EndIf
    SetGadgetText(#mainTxt_SourceDir, SourceDir)
    StatusBarText(#mainStatusbar, 0, "Scanning source directory ...")
    time = ElapsedMilliseconds()
    GetDirectoryContent(SourceFileList(), SourceDir, OptionIncSubdirs, OptionIncDirs)
    SetGadgetText(#mainTxt_SourceFilesNo, Str(CountFilesInList(SourceFileList())))
    SetGadgetText(#mainTxt_SourceDirsNo, Str(CountDirsInList(SourceFileList())))
    FinishList(SourceFileList(), SourceDir)
    time = ElapsedMilliseconds() - time
    SetGadgetState(#mainProgressbar, 100)
    StatusBarText(#mainStatusbar, 0, "Done, took " + Str(time) + " miliseconds.")
  EndIf
EndProcedure
Procedure SelectDestDir()
  Shared DestFileList()
  Shared DestDir
  Shared OptionIncDirs
  Shared OptionIncSubdirs
  Define time.i
  DestDir = PathRequester("Select destination directory", ".")
  If DestDir <> ""
    If ListSize(DestFileList())
      ClearList(DestFileList())
    EndIf
    SetGadgetText(#mainTxt_DestDir, DestDir)
    StatusBarText(#mainStatusbar, 0, "Scanning destination directory ...")
    time = ElapsedMilliseconds()
    GetDirectoryContent(DestFileList(), DestDir, OptionIncSubdirs, OptionIncDirs)
    SetGadgetState(#mainProgressbar, 50)
    SetGadgetText(#mainTxt_DestFilesNo, Str(CountFilesInList(DestFileList())))
    SetGadgetText(#mainTxt_DestDirsNo, Str(CountDirsInList(DestFileList())))
    FinishList(DestFileList(), DestDir)
    time = ElapsedMilliseconds() - time
    SetGadgetState(#mainProgressbar, 100)
    StatusBarText(#mainStatusbar, 0, "Done, took " + Str(time) + " miliseconds.")
  EndIf
EndProcedure
Procedure SelectCheckButton()
  Shared SourceFileList()
  Shared SourceDir
  Shared DestFileList()
  Shared DestDir
  Shared DiffFileList()
  Define time.i
  StatusBarText(#mainStatusbar, 0, "Checking directories for differences ...")
  time = ElapsedMilliseconds()
  CompareLinkedLists(SourceFileList(), SourceDir, DestFileList(), DestDir, DiffFileList())
  time = ElapsedMilliseconds() - time
  SetGadgetState(#mainProgressbar, 100)
  StatusBarText(#mainStatusbar, 0, "Done, took " + Str(time) + " miliseconds.")
  If ListSize(DiffFileList()) > 0
    SetGadgetText(#mainTxt_Sync, Str(ListSize(DiffFileList())) + " files and dirs are missing in destination directory")
    DisableGadget(#mainBtn_ShowDiff, #False)
    DisableGadget(#mainBtn_Sync, #False)
  Else
    SetGadgetText(#mainTxt_Sync, "No files or dirs are missing in destination directory")
  EndIf
EndProcedure
Procedure SelectDiffButton()
  Shared DiffFileList()
  If IsWindow(#toolWnd)
    CloseWindow(#toolWnd)
    SetGadgetText(#mainBtn_ShowDiff, "Show Differences")
  Else
    OpenDifferenceWindow()
    ForEach DiffFileList()
      AddGadgetItem(#toolLst_Diff, -1, DiffFileList()\path + DiffFileList()\name)
    Next
    SetGadgetText(#mainBtn_ShowDiff, "Hide Differences")
  EndIf
EndProcedure
Procedure.b SelectSyncButton()
  Shared DiffFileList()
  Shared SourceDir
  Shared DestDir
  NewList errorlist.DirectoryEntryDescriptor()
  Define size.f
  Define time.i
  If ListSize(DiffFileList()) = 0
    ProcedureReturn #False
  EndIf
  If Right(SourceDir, 1) <> #Slash
    SourceDir = SourceDir + #Slash
  EndIf
  If Right(DestDir, 1) <> #Slash
    DestDir = DestDir + #Slash
  EndIf
  size = ListSize(DiffFileList()) / 100
  StatusBarText(#mainStatusbar, 0, "Copy missing source files to destination ...")
  time.i = ElapsedMilliseconds()
  ForEach DiffFileList()
    If DiffFileList()\isdir
      Continue
    EndIf
    CheckDestinationPath(DiffFileList()\path)
    If Not CopyFile(DiffFileList()\path + DiffFileList()\name, ReplaceString(DiffFileList()\path, SourceDir, DestDir) + DiffFileList()\name)
      AddElement(errorlist())
      errorlist()\path = DiffFileList()\path
      errorlist()\name = DiffFileList()\name
    EndIf
    SetGadgetState(#mainProgressbar, Round(ListIndex(DiffFileList()) / size, #PB_Round_Nearest))
  Next
  time = ElapsedMilliseconds() - time
  StatusBarText(#mainStatusbar, 0, "Done, took " + Str(time) + " miliseconds.")
  ClearList(DiffFileList())
  CopyList(errorlist(), DiffFileList())
  ClearList(errorlist())
  If ListSize(DiffFileList()) > 0 
    SetGadgetText(#mainTxt_Sync, "There are errors occured during the copy process.")
    SetGadgetText(#mainBtn_ShowDiff, "Show Errors")
  Else
    SetGadgetText(#mainTxt_Sync, "")
    DisableGadget(#mainBtn_ShowDiff, #True)
  EndIf
  DisableGadget(#mainBtn_Sync, #True)
  OpenDestDir()
EndProcedure

;- Main

LoadSettings()

OpenMainWindow()

CheckForThread = #True
SourceThreadID = CreateThread(@OpenSourceDir(), #True)
DestThreadID   = CreateThread(@OpenDestDir(), #True)


Repeat
  WindowEvent = WaitWindowEvent(100)
  WindowID    = EventWindow()
  GadgetID    = EventGadget()
  If WindowEvent = #PB_Event_Gadget
    If GadgetID = #mainBtn_DirSource
      SelectSourceDir()
    ElseIf GadgetID = #mainBtn_DirDest
      SelectDestDir()
    ElseIf GadgetID = #mainBtn_Check
      SelectCheckButton()
    ElseIf GadgetID = #mainBtn_ShowDiff
      SelectDiffButton()
    ElseIf GadgetID = #mainBtn_Sync
      SelectSyncButton()
    ElseIf GadgetID = #mainChk_incDirs
      If OptionIncDirs
        OptionIncDirs = #False
      Else
        OptionIncDirs = #True
      EndIf
    ElseIf GadgetID = #mainChk_incSubdirs
      If OptionIncSubdirs
        OptionIncSubdirs = #False
      Else
        OptionIncSubdirs = #True
        OptionIncDirs = #True
        SetGadgetState(#mainChk_incDirs, #True)
      EndIf
    ElseIf GadgetID = #mainChk_incSystem
      If OptionIncSysfiles
        OptionIncSysfiles = #False
      Else
        OptionIncSysfiles = #True
      EndIf
    ElseIf GadgetID = #mainChk_incHidden
      If OptionIncHidfiles
        OptionIncHidfiles = #False
      Else
        OptionIncHidfiles = #True
      EndIf
    EndIf
  ElseIf WindowEvent = #PB_Event_CloseWindow
    If WindowID = #toolWnd
      SelectDiffButton()
    ElseIf WindowID = #mainWnd
      QUIT = #True
    EndIf
  EndIf
  If Not IsThread(SourceThreadID) And Not IsThread(DestThreadID) And CheckForThread
    CheckForThread = #False
    DisableGadget(#mainBtn_Check, #False)
    StatusBarText(#mainStatusbar, 0, "Directory scanning done.")
  EndIf
Until QUIT = #True
;- End
If IsThread(SourceThreadID)
  KillThread(SourceThreadID)
EndIf
If IsThread(DestThreadID)
  KillThread(DestThreadID)
EndIf
SaveSettings()
End  
---
Windows 11 (64 bit)
twofel
Beiträge: 18
Registriert: 20.03.2013 13:34

Re: Zwei Linked Lists vergleichen

Beitrag von twofel »

Hallo,

kriege beim kompilieren immer folgenden Fehler:

PreferenceComment(#APPNAME + " v" + Str(#APPMAJOR) + "." + Str(#APPMINOR) + "." + Str(#PB_Editor_BuildCount) + " config file")


[10:21:13] [COMPILER] Zeile 150: Konstante nicht gefunden: #PB_Editor_BuildCount.
Antworten