在递归中返回值
本文关键字:返回值 递归 | 更新日期: 2023-09-27 18:12:47
我的问题"现代语言如何处理局部变量和递归?"来自于编写一个文件搜索方法。基本上,
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]);
}
因此,问题是,这将在每次递归中重新声明"songpath"。在VB中,您可以将歌曲路径声明为静态,我认为这将解决此问题。有没有一个好的C#方法可以让我不覆盖ArrayList?
请注意,我的操作假设您打算使用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>
访问数组。
您可以选择使用累加器策略,并将工作强加于需要将数组传递给它的辅助函数:
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);
}
}
}
如果你在.Net 4.0+上,这就变成了Directory.EnumerateFiles
:的小菜一碟
public IEnumerable<string> GetMusicFiles(string directory)
{
return Directory.EnumerateFiles(directory, "*.*", SearchOption.AllDirectories)
.Where(ff => IsMusicFile(ff));
}
代码有点难读,但无论如何。。。我建议编写一个实际用于递归的方法的私有版本,并将ArrayList作为参数传递。
您可以通过传入当前列表来实现这一点,这样您就可以在递归中附加列表。像这样的。。。
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;
}
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()
。