2013-05-07 120 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)); 
}