구현할 때 재귀와 반복의 문제를 고민할 때가 있습니다. 제 경험으로는 반복보다는 재귀로 작성하는게 훨 쉽습니다. 그리고 이게 더 코드가 깔끔하다고 생각했었는데 이 재귀를 다시 반복으로 구현하는 것도 나쁘지 않다는 생각을 갖게 되었습니다.
왜 이런 재귀와 반복의 변환을 고민하게 되었냐면은 재귀로 작성했던 기능이 있는데 이 기능이 함수 하나로만 작성되어야할 필요가 생겼습니다. 그래서 내키지 않았지만 어쩔수 없이 반복으로 재작성하였습니다. 해놓고 보니 나쁘지 않더군요.
재귀와 반복 구현에 대해 마이크로소프트에서 제공하는 아주 좋은 예제가 있어 공유해 드립니다.
출처: http://msdn.microsoft.com/ko-kr/library/bb513869.aspx
디렉토리 탐색하기
1. 재귀를 이용한 방법 (Recusive File Search)
2. 반복을 이용한 방법 (Stack based Iteration)
왜 이런 재귀와 반복의 변환을 고민하게 되었냐면은 재귀로 작성했던 기능이 있는데 이 기능이 함수 하나로만 작성되어야할 필요가 생겼습니다. 그래서 내키지 않았지만 어쩔수 없이 반복으로 재작성하였습니다. 해놓고 보니 나쁘지 않더군요.
재귀와 반복 구현에 대해 마이크로소프트에서 제공하는 아주 좋은 예제가 있어 공유해 드립니다.
출처: http://msdn.microsoft.com/ko-kr/library/bb513869.aspx
디렉토리 탐색하기
1. 재귀를 이용한 방법 (Recusive File Search)
public class RecursiveFileSearch { static System.Collections.Specialized.StringCollection log = new System.Collections.Specialized.StringCollection(); static void Main() { // Start with drives if you have to search the entire computer. string[] drives = System.Environment.GetLogicalDrives(); foreach (string dr in drives) { System.IO.DriveInfo di = new System.IO.DriveInfo(dr); // Here we skip the drive if it is not ready to be read. This // is not necessarily the appropriate action in all scenarios. if (!di.IsReady) { Console.WriteLine("The drive {0} could not be read", di.Name); continue; } System.IO.DirectoryInfo rootDir = di.RootDirectory; WalkDirectoryTree(rootDir); } // Write out all the files that could not be processed. Console.WriteLine("Files with restricted access:"); foreach (string s in log) { Console.WriteLine(s); } // Keep the console window open in debug mode. Console.WriteLine("Press any key"); Console.ReadKey(); } static void WalkDirectoryTree(System.IO.DirectoryInfo root) { System.IO.FileInfo[] files = null; System.IO.DirectoryInfo[] subDirs = null; // First, process all the files directly under this folder try { files = root.GetFiles("*.*"); } // This is thrown if even one of the files requires permissions greater // than the application provides. catch (UnauthorizedAccessException e) { // This code just writes out the message and continues to recurse. // You may decide to do something different here. For example, you // can try to elevate your privileges and access the file again. log.Add(e.Message); } catch (System.IO.DirectoryNotFoundException e) { Console.WriteLine(e.Message); } if (files != null) { foreach (System.IO.FileInfo fi in files) { // In this example, we only access the existing FileInfo object. If we // want to open, delete or modify the file, then // a try-catch block is required here to handle the case // where the file has been deleted since the call to TraverseTree(). Console.WriteLine(fi.FullName); } // Now find all the subdirectories under this directory. subDirs = root.GetDirectories(); foreach (System.IO.DirectoryInfo dirInfo in subDirs) { // Resursive call for each subdirectory. WalkDirectoryTree(dirInfo); } } } }
2. 반복을 이용한 방법 (Stack based Iteration)
public class StackBasedIteration { static void Main(string[] args) { // Specify the starting folder on the command line, or in // Visual Studio in the Project > Properties > Debug pane. TraverseTree(args[0]); Console.WriteLine("Press any key"); Console.ReadKey(); } public static void TraverseTree(string root) { // Data structure to hold names of subfolders to be // examined for files. Stack<string> dirs = new Stack<string>(20); if (!System.IO.Directory.Exists(root)) { throw new ArgumentException(); } dirs.Push(root); while (dirs.Count > 0) { string currentDir = dirs.Pop(); string[] subDirs; try { subDirs = System.IO.Directory.GetDirectories(currentDir); } // An UnauthorizedAccessException exception will be thrown if we do not have // discovery permission on a folder or file. It may or may not be acceptable // to ignore the exception and continue enumerating the remaining files and // folders. It is also possible (but unlikely) that a DirectoryNotFound exception // will be raised. This will happen if currentDir has been deleted by // another application or thread after our call to Directory.Exists. The // choice of which exceptions to catch depends entirely on the specific task // you are intending to perform and also on how much you know with certainty // about the systems on which this code will run. catch (UnauthorizedAccessException e) { Console.WriteLine(e.Message); continue; } catch (System.IO.DirectoryNotFoundException e) { Console.WriteLine(e.Message); continue; } string[] files = null; try { files = System.IO.Directory.GetFiles(currentDir); } catch (UnauthorizedAccessException e) { Console.WriteLine(e.Message); continue; } catch (System.IO.DirectoryNotFoundException e) { Console.WriteLine(e.Message); continue; } // Perform the required action on each file here. // Modify this block to perform your required task. foreach (string file in files) { try { // Perform whatever action is required in your scenario. System.IO.FileInfo fi = new System.IO.FileInfo(file); Console.WriteLine("{0}: {1}, {2}", fi.Name, fi.Length, fi.CreationTime); } catch (System.IO.FileNotFoundException e) { // If file was deleted by a separate application // or thread since the call to TraverseTree() // then just continue. Console.WriteLine(e.Message); continue; } } // Push the subdirectories onto the stack for traversal. // This could also be done before handing the files. foreach (string str in subDirs) dirs.Push(str); } }}