воскресенье, 6 мая 2012 г.

Статья о рекурсии: статья о рекурсии: сатья...


Недавно возникла потребность написать небольшой код, который будет содержать рекурсию. Задача была довольно таки простой: вывести содержимое папки до самого последнего уровня вложенности. Важное требование - древовидная форма вывода. Дня 3 ходил и прикидывал в голове, какой будет метод для рекурсивной функции... В голове сформировалось даже несколько подходов и каждый из них был более чем рабочим.

Само-собой пришло время сесть и написать хотя бы одну реализацию. Сел, раскрыл Java API для работы с File'ами. Набросал несколько небольших семплов и решил, что этого уже достаточно для решения задачи... Чего я только не написал за тот вечер, было все, что угодно, только не решение. До бешенства доводили бесконечные циклы и ничего с этим поделать нельзя было.

Решил оставить написание кода на утро.

Создал два класса:

import java.io.File;

public class Recurse {
    
    int level = 0;
    String spaces = "";
    
    public void starter(String path) {
        File rootDir = new File(path);
        if (rootDir.isFile())
            System.out.println(spaces+"[F] "+rootDir.getName());
        if (rootDir.isDirectory()) {
            System.out.println(spaces+"[D] "+rootDir.getName());
            File[] innerList = rootDir.listFiles();
            if (innerList.length > 0) {
                level++;
                spaces += "  ";
                for (File f : innerList) {
                    starter(f.getPath());
                }
            }
            level--;
            spaces = spaces.substring(0, spaces.length()-1);
        }
    }
}


public class FilePolygon {    
    public static void main(String[] args) {
        Recurse r = new Recurse();        
        do {
            r.starter("E:\\RootDir");
        } while (r.level > 0);        
    }
}

К моему большому удивлению, все получилось с первого раза. Поэтому я рассчитываю, что этот код может содержать ошибки. Тестил свою рекурсию на этой папке. Можете ее выкачать и попробовать провести свои испытания. Буду рад прочитать отзывы.

UPDATE:

Огромное спасибо Андрею Нестеренко за оперативный отзыв и внесение замечаний по работе метода. После ряда правок, получаем новый вариант кода, который более лаконичен и не выдает ошибок при некоторых структурах директорий и файлов в них.

import java.io.File;

public class Recurse {
    
    String spaces = "";
    
    public void starter(String path) {
        File rootDir = new File(path);
        if (rootDir.isFile())
            System.out.println(spaces+"[F] "+rootDir.getName());
        if (rootDir.isDirectory()) {
            System.out.println(spaces+"[D] "+rootDir.getName());
            
            File[] innerList = rootDir.listFiles();
            
            if (innerList.length == 0)
                return;
            
            spaces += "  ";
            for (File f : innerList) {
                starter(f.getPath());
            }
            
            spaces = spaces.substring(0, spaces.length()-2);
        }
    }
}

public class FilePolygon {    
    public static void main(String[] args) {
        Recurse r = new Recurse();        
        r.starter("E:\\RootDir");            
    }
}

Результат улучшился налицо! Ушла такая переменная, как int level. Появился оператор return, который в случае нулевой длины innerList, передаст управление вызывающему его объекту.

Если ваша программа заработала с первого раза, то без сомнений сообщите создателям компилятора, что у них продукт с дефектом ©

6 комментариев:

  1. С виду все правильно кроме одного. К spaces добавляется 2 пробела, а удаляется только один.

    ОтветитьУдалить
    Ответы
    1. Оказывается только с виду все было правильно =)

      Удалить
  2. А команда "ls -R" вижу вам чужда?

    ОтветитьУдалить
  3. Леша, довольно-таки интересную тему затронул :)
    Не дал покоя мне твой код и на досуге решил покопаться в нем. Вылетает эксепшн при такой структуре каталога:
    RootDir
       Dir1
       Dir2
          Dir21
          file.txt
       Dir3

    Плюс к этому, если директория не содержала бы вообще каталогов, то программа вылетала также.

    Мне было интересно, как можно изменить твой код (улучшить, укоротить, убрать эксепшены) и в итоге, я немного подкорректировал код класса Recurse и получил следующую версию:
    import java.io.File;
    public class Recurse {
       public void starter(String path) {
          String spaces = "";
          File rootDir = new File(path);
          if (rootDir.isFile())
          {
             System.out.println(spaces+"[F] "+rootDir.getName());
          }
          else
          {
             System.out.println(spaces+"[D] "+rootDir.getName());
             File[] innerList = rootDir.listFiles();

             if (innerList.length == 0)
             {
                return;
             }

             spaces += " ";
             for (File f : innerList)
             {
                starter(f.getPath());
             }
             spaces = spaces.substring(0, spaces.length() - 2);
             }
       }
    }

    ОтветитьУдалить
  4. к сожалению blogspot не позволяет редактировать собственные комментарии( я чуть ошибся, скопировав не то. Вот эта версия правильная:

    класс Recurse:

    import java.io.File;
    public class Recurse {
       String spaces = "";

       public void starter(String path) {
          File rootDir = new File(path);
          if (rootDir.isFile())
          {
             System.out.println(spaces+"[F] "+rootDir.getName());
          }
          else
          {
             System.out.println(spaces+"[D] "+rootDir.getName());
             File[] innerList = rootDir.listFiles();

             if (innerList.length == 0)
                return;

             spaces += " ";
             for (File f : innerList)
             {
                starter(f.getPath());
             }
             spaces = spaces.substring(0, spaces.length() - 2);
          }
       }
    }

    Класс FilePolygon:

    public class FilePolygon {
       public static void main(String[] args) {
          Recurse r = new Recurse();
          r.starter("D:\\RootDir");   }
    }

    ОтветитьУдалить
    Ответы
    1. Спасибо за обширный коммент =)

      Ставлю себе TODO пункт на обновление своего поста, согласно твоим замечаниям =)

      В ближайшие дни будет апдейт =)

      Удалить