2013-05-07 241 views
0

我的問題「現代語言如何處理局部變量和遞歸?」來自編寫文件搜索方法。基本上,遞歸返回值

public ArrayList getMusicFiles(string directory){ 
    ArrayList songpaths = new ArrayList(); 

    string[] localFiles = System.IO.Directory.GetFiles(directory); 
    for(int i=0; i<localFiles.Length-1; i++) 
     if(isMusicFile(localFiles[i])) 
      songpaths.add(localFiles[i]); 

    string[] localFolders = System.IO.Directory.GetDirectories(directory); 
    for(int i=0; i<localFolder.length-1; i++) 
     getMusicFiles(localFolder[i]); 
} 

所以,問題是這將重新聲明「songpaths」與每個遞歸。在VB中,你可以聲明歌曲路徑爲靜態,我認爲這可以解決這個問題。有沒有一個很好的C#方式,我不覆蓋ArrayList?

+0

爲什麼有人會在這裏使用'ArrayList'而不是'List '? – ja72 2013-05-07 22:38:25

+0

你對'directory'參數做了什麼? – cdhowie 2013-05-07 22:40:52

回答

-1
public ArrayList getMusicFiles(string directory, ArrayList songpaths){ 
    string[] localFiles= System.Io.Directory.GetFiles(rootDir); 

    for(int i=0; i<localFiles.Length-1; i++) if(isMusicFile(localFiles[i])) 
     songpaths.add(localFiles[i]); 

    string[] localFolders= System.IO.Directory.GetDirectories(rootDir); 
for(int i=0; i<localFolder.length-1; i++) getMusicFiles(localFolder[i]); 

然後當你第一次調用該方法時,你通過了new ArrayList()

1

該代碼有點難以閱讀,但無論如何......我建議編寫一個實際用於遞歸的方法的私有版本,並將ArrayList作爲參數傳遞。

8

請注意,我的操作假定您打算使用directory參數來代替rootDir類級變量。


這裏有兩個選項。

的哈克,但更高效的方式

在這種情況下,您通過列表對象。我將使用List<string>而不是ArrayList

public List<string> getMusicFiles(string directory) { 
    var list = new List<string>(); 
    getMusicFiles(list, directory); 
    return list; 
} 

private void getMusicFilesInternal(List<string> songpaths, string directory) 
{ 
    string[] localFiles= System.IO.Directory.GetFiles(directory); 
    for(int i=0; i<localFiles.Length-1; i++) { 
    if(isMusicFile(localFiles[i])) { 
     songpaths.add(localFiles[i]); 
    } 
    } 
    string[] localFolders= System.IO.Directory.GetDirectories(directory); 
    for(int i=0; i<localFolder.Length-1; i++) { 
    getMusicFiles(songpaths, localFolder[i]); 
    } 
} 

的效率較低,但功能性的方式

返回列表,每次聚集的結果,你遞歸:

public IList<string> getMusicFiles(string directory) 
{ 
    List<string> songpaths = new List<string>(); 
    string[] localFiles= System.IO.Directory.GetFiles(directory); 
    for(int i=0; i<localFiles.Length-1; i++) { 
    if(isMusicFile(localFiles[i])) { 
     songpaths.add(localFiles[i]); 
    } 
    } 
    string[] localFolders= System.IO.Directory.GetDirectories(directory); 
    for(int i=0; i<localFolder.Length-1; i++) { 
    songpaths.AddRange(getMusicFiles(localFolder[i])); 
    } 
    return songpaths; 
} 

您還可以實現這一點使用延遲執行,這仍然是不如第一個例子那麼高效,但是爲您提供更多的靈活性,使您能夠使用結果:

public IEnumerable<string> getMusicFiles(string directory) 
{ 
    string[] localFiles= System.IO.Directory.GetFiles(directory); 
    for(int i=0; i<localFiles.Length-1; i++) { 
    if(isMusicFile(localFiles[i])) { 
     yield return localFiles[i]; 
    } 
    } 
    string[] localFolders= System.IO.Directory.GetDirectories(directory); 
    for(int i=0; i<localFolder.Length-1; i++) { 
    foreach (var j in getMusicFiles(localFolder[i])) { 
     yield return j; 
    } 
    } 
} 

這將返回一個枚舉值,它將執行搜索操作,每次您枚舉它,類似於Linq查詢的工作方式。您可以在結果上調用ToList()以執行查詢並將結果存儲在列表中,您可以多次枚舉而不必再次執行查詢。

如果我清理了所有的代碼,以下是我可能會選擇使用的變體。您的原始代碼有幾個問題(您從陣列的Length屬性中減去一個,即使這會導致您跳過最後一個元素,並且還有其他一些拼寫錯誤)。

public IEnumerable<string> getMusicFiles(string directory) 
{ 
    foreach (var file in System.IO.Directory.GetFiles(directory)) { 
    if (isMusicFile(file)) { 
     yield return file; 
    } 
    } 

    foreach (var dir in System.IO.Directory.GetDirectories(directory)) { 
    foreach (var musicFile in getMusicFiles(dir)) { 
     yield return musicFile; 
    } 
    } 
} 

如果您擔心foreach的性能,請不要。首先,您應該首先對可讀性和性能進行編碼,只有在發現瓶頸時才進行優化。其次,當你在一個數組類型上使用foreach時,編譯器會將它變成等效的基於Length的迭代,而不是通過IEnumerator<T>來訪問該數組。

+0

你是怎麼在2分鐘內想出來的? – Jasmine 2013-05-07 22:38:07

+0

@茉莉花咖啡因。 :) – cdhowie 2013-05-07 22:39:18

+0

+1,但'Directory.GetFiles(path,「*。*」,SearchOption.AllDirectories)'不需要遞歸。 – user7116 2013-05-07 22:45:55

0

你可以通過傳遞當前列表來完成 - 這樣你可以在遞歸中追加列表。像這樣的東西...

public ArrayList getMusicFiles(string directory, ArrayList data, string rootDir) { 
    string[] localFiles = System.IO.Directory.GetFiles(rootDir); 
    for (int i = 0; i < localFiles.Length - 1; i++) 
     if (isMusicFile(localFiles[i])) 
      songpaths.add(localFiles[i]); 

    string[] localFolders = System.IO.Directory.GetDirectories(rootDir); 
    for (int i = 0; i < localFolder.length - 1; i++) 
     data.AddRange(getMusicFiles(localFolder[i])); 
    return data; 
} 
2

你可以選擇使用蓄電池的策略,並強加於工作的輔助函數需要數組傳遞給它:

public List<string> GetMusicFiles(string directory) 
{ 
    List<string> songPaths = new List<string>(); 

    GetMusicFilesHelper(directory, songPaths); 

    return songPaths; 
} 

private void GetMusicFilesHelper(string directory, List<string> paths) 
{ 
    string[] localFiles = Directory.GetFiles(directory); 
    for(int i = 0; i < localFiles.Length; i++) 
    { 
     if(isMusicFile(localFiles[i])) paths.Add(localFiles[i]); 
    } 

    string[] localFolders = Directory.GetDirectories(directory); 
    for(int i = 0; i < localFolder.length; i++) 
    { 
     GetMusicFilesHelper(localFolder[i], paths); 
    } 
} 

另一種選擇是跳過遞歸一起,讓Directory.GetFiles爲你做的工作:

public List<string> GetMusicFiles(string directory) 
{ 
    List<string> songPaths = new List<string>(); 

    // TODO: pick a better search pattern 
    string[] paths = Directory.GetFiles(directory, "*.*", SearchOption.AllDirectories); 
    foreach (string path in paths) 
    { 
     if (IsMusicFile(path)) 
     { 
      songPaths.Add(path); 
     } 
    } 
} 

如果你在淨4.0+,這成爲與Directory.EnumerateFiles小菜一碟:

public IEnumerable<string> GetMusicFiles(string directory) 
{ 
    return Directory.EnumerateFiles(directory, "*.*", SearchOption.AllDirectories) 
        .Where(ff => IsMusicFile(ff)); 
}