Re[22]: Насколько важен синтаксис языка?
От: Sinclair Россия https://github.com/evilguest/
Дата: 07.09.06 22:21
Оценка: 11 (3) +2 -1
Здравствуйте, Дарней, Вы писали:
Д>лучще приведи пример кода, где не жить не быть — но без нелокальных возвратов обойтись нельзя
Да нет такого кода. Но смысл я понял.
Там, где дотнету приходится делать два метода: FindFirst и Apply, смоллток обходится одним. Просто потому, что решение о том, продолжать итерацию или выйти "изо всего" принимается в "делегате".
В более сложных случаях давайте вспомним, как мы делали поиск по дереву. Конечно же рекурсия; и конечно же надо было обязательно оборудовать ее проверкой на продолжение и сохранением найденного элемента...
Если бы нелокальный возврат существовал бы в дотнете, это работало бы как-то так:
public interface IRecursible<T>: IEnumerable<IRecursible<T>>;

public static void ForEach<T>(T root, Action<T> action)
  where T: IRecursible<T>
{
  action(root);
  foreach(T child in root)
      ForEach(child, action);
}


Это мы так один раз объявили служебный метод. Его можно применять чтобы, например, выводить все дерево:

class MyItem: IRecursible<MyItem>
{
  // тривиальная реализация интерфейса поскипана
    public string Name;
}

public static PrintAll(MyItem root)
{
  ForEach(root, delegate(MyItem t) { Console.WriteLine(t.Name);});
}

Тривиально, не правда ли? А теперь давайте кого-нибудь найдем:

public static MyItem FindFirstStartingWith(MyTtem root, string start)
{
  ForEach(root, 
        delegate(MyItem t) 
        { 
            if (t.Name.StartsWith(start))
                super return t;
        } 
    );
}

Здесь я применил "новый" оператор super return, чтобы намекнуть на нелокальный возврат (в отличие от обычного return, который все равно приведет к ошибке компиляции из-за несовпадения типа получившегося анонимого делегата с Action<MyItem>).
На что стоит обратить внимание?
1. Все очевидно. Если знать, что такое супер-ретён.
2. Никакой нелокальности с точки зрения пользователя нет. Код — немногим хуже банального foreach.
3. При этом все-таки возврат может случиться с произвольной глубины стека — ведь Apply вызывает себя рекурсивно...
4. Мы обошлись без написания специального метода поиска в дереве, типа вот такого:

public static bool FindFirst(T root, Predicate<T> condition, out T result)
  where T: IRecursible<T>
{
  
  if(condition(root))
    {
      result = root;
        return true;
    }
    foreach(T child in root)
    {
      if (FindFirst(child, condition, out result))
            return true;
    }
    return false;
}

public static MyItem FindFirstStartingWith(MyItem root, string start)
{
  MyItem result;
  if (FindFirst(root, delegate(MyItem t) { return t.Name.StartsWith(start);}, out result))
      return result
    else
        return null;
}


5. На самом деле в данном случае не нужен весь этот лишний мусор в виде FindFirst. И ForEach тоже не нужен. ForEach уже сделан. Им и надо пользоваться в таких случаях:
public static IEnumerable<T> IterateThrough<T>(T root)
    where T: IRecursible<T>
{
  yield return root;
    foreach(T child in root)
      foreach(T grandchild in IterateThrough(child))
          yield return grandchild;
}

public static void PrintAll(MyItem root)
{
  foreach(MyItem item in IterateThrough(root))
      Console.WriteLine(item.Name);
}
public static MyItem FindFirstStartingWith(MyItem root, string start)
{
  foreach(MyItem item in IterateThrough(root))
      if (item.Name.StartsWith(start)
            return item;
    return null;
}
1.1.4 stable rev. 510
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.