Re[7]: Какой код проще, лучше и почему
От: Undying Россия  
Дата: 06.04.10 10:34
Оценка:
Здравствуйте, Silver_s, Вы писали:

U>>Объясни каким образом рефакторинг поможет избавиться от этих промежуточных состояний?


S_>Если для написавшего ее, она легко читается и ее не прийдется менять, то никаких проблем. Если нет то ее лучше было бы как-то измельчить, порубать на более мелкие. Неважно в функциональном стиле или нет. Для алгоритмов как правило всегда существует много вариантов реализации.


Объясни каким образом измельчение позволит из императивного кода сделать функциональный?

S_>Например:Тут вот похоже два потока данных rawArrivals, rawDepartures, каждый оборот внутреннего цикла выдергивает и обрабатывает один элемент либо из первого либо из второго потока. Это уже можно вынести, в отдельную функцию возвращающую перечисление currentInterval, и если надо информацию откуда выдернуто.Небольшая но все-таки декомпозиция. Не факт что это сильно улучшит, остального кода я не видел (да и не стал бы разбираться с пол-тысячей строк).


И в чем после этого код станет менее императивным?

S_> Всю задачу я не понимаю. Но есть опасения что приведенный кусок кода выдернут из функции которая раз в 5 больше этого куска.


Приведенный кусок кода это практически функция целиком, единственно показана обработка только первой возможной ситуации (транспорт движется запланированно), а обработка двух других возможных ситуаций (неожиданная остановка и транспорт пропустил несколько остановок, но дальше поехал запланированно) поскипана, т.к. не суть.

S_> Интересно было бы только время жизни этих данных.


Время жизни от нуля (если на момент вызова функции у нас были данные на конец расписания) до бесконечности (если данные на конец расписания нам еще не пришли).

S_> И есть ли какая-то топ-функция у который вполне конкретный вход и выход, которая вызывает остальные функции, и которая скрывает какие-то промежуточные данные(время жизни которых не выходит за пределы этой функции).


Это и есть топ-функция. На вход она принимает въезды и выъезды на остановки, на выходе возвращает остановки с временем их прохождения.
Re[8]: 2AVK: С чем несогласен?
От: Undying Россия  
Дата: 06.04.10 10:34
Оценка:
Здравствуйте, AndrewVK, Вы писали:

U>>Можно пример сложного алгоритма без сложного промежуточного состояния?


AVK>В каком виде пример? Форматтер исходников в ешарпере сгодится?


Ссылку можно?
Re[9]: 2AVK: С чем несогласен?
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 06.04.10 11:04
Оценка:
Здравствуйте, Undying, Вы писали:

U>Ссылку можно?


Ссылку на что? На решарпер?
... << RSDN@Home 1.2.0 alpha 4 rev. 1469 on Windows 7 6.1.7600.0>>
AVK Blog
Re[10]: 2AVK: С чем несогласен?
От: Undying Россия  
Дата: 06.04.10 11:06
Оценка:
Здравствуйте, AndrewVK, Вы писали:

AVK>Ссылку на что? На решарпер?


На код, который не имеет промежуточных состояний, но является алгоритмически сложным.
Re[11]: 2AVK: С чем несогласен?
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 06.04.10 11:08
Оценка:
Здравствуйте, Undying, Вы писали:

U>На код, который не имеет промежуточных состояний, но является алгоритмически сложным.


Код ты можешь поглядеть только в декомпиляторе.
... << RSDN@Home 1.2.0 alpha 4 rev. 1469 on Windows 7 6.1.7600.0>>
AVK Blog
Re[3]: Какой код проще, лучше и почему
От: Undying Россия  
Дата: 06.04.10 11:16
Оценка:
Здравствуйте, gandjustas, Вы писали:

U>>Главная проблема чисто функционального подхода, что на нем непонятно как писать сложные алгоритмы.

U>>Вот к примеру алгоритм определения прохождения расписания:
G>По человечески объясни что код делает, уверен что моментально напишут функциональное решение.

Контроль расписания транспорта(например, автобуса). Т.е. есть набор остановок с временами когда автобус должен на них находиться (например, остановка А-выезд 7:45, остановка B-прибытие 8:34, остановка B-выезд 8:40, остановка А-прибытие 9:05, остановка А-выезд 9:12 и т.д.). Остановки могут накладываться друг друга. Нужно получить отклонения автобуса от расписания.
Re[4]: Какой код проще, лучше и почему
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 06.04.10 11:24
Оценка:
Здравствуйте, Undying, Вы писали:

U>Здравствуйте, gandjustas, Вы писали:


U>>>Главная проблема чисто функционального подхода, что на нем непонятно как писать сложные алгоритмы.

U>>>Вот к примеру алгоритм определения прохождения расписания:
G>>По человечески объясни что код делает, уверен что моментально напишут функциональное решение.

U>Контроль расписания транспорта(например, автобуса). Т.е. есть набор остановок с временами когда автобус должен на них находиться (например, остановка А-выезд 7:45, остановка B-прибытие 8:34, остановка B-выезд 8:40, остановка А-прибытие 9:05, остановка А-выезд 9:12 и т.д.).

Так уже лучше.
Какие входные данные? Как определять нахождение автобуса на остановке? В случае непопадания во время прибытия автобус должен простоять положенное время или должен уехать вовремя?

U>Остановки могут накладываться друг друга.

Это как

U>Нужно получить отклонения автобуса от расписания.

В каком виде?
Re[5]: Какой код проще, лучше и почему
От: Undying Россия  
Дата: 07.04.10 10:45
Оценка:
Здравствуйте, gandjustas, Вы писали:

U>>Контроль расписания транспорта(например, автобуса). Т.е. есть набор остановок с временами когда автобус должен на них находиться (например, остановка А-прямая 7:45, остановка B-прямая 8:34, остановка B-обратная 8:40, остановка А-обратная 9:05, остановка А-прямая 9:12 и т.д.).


G>Какие входные данные?


Исходные, то что выше + геокоординаты автобуса во времени. В том алгоритме который я приводил, вместо геокоординат автобуса во времени, алгоритм работает с интервалами времен нахождения в пределах остановки (подготавливаемых из геокоординат автобуса во времени другой функцией).

G>Как определять нахождение автобуса на остановке?


Остановка это геокоордината + радиус. Попадание в этот радиус есть признак нахождения автобуса в пределах остановки. Соответственно алгоритм должен отбрасывать ложные проезды остановок (например, двигаясь по прямому маршруту автобус зацепил остановку обратного маршрута), а также определять к какому времени расписания проезд остановки относился.

G>В случае непопадания во время прибытия автобус должен простоять положенное время или должен уехать вовремя?


Это водителю решать. Задача алгоритма честно показать все отклонения автобуса от расписания.

U>>Остановки могут накладываться друг друга.

G>Это как

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

U>>Нужно получить отклонения автобуса от расписания.

G>В каком виде?

В приведенном алгоритме, у нас есть коллекция остановок расписания с планируемыми временами (описанная вверху), итогом работы алгоритма является выставление для каждой остановки реального времени прибытия и отправления.

ps
Я не требую реализовывать функционально конкретно этот алгоритм (это просто пример задачи со сложным промежуточным состоянием), но мне было бы интересно увидеть решение любой вашей задачи со сложным промежуточным состоянием в чисто функциональном стиле.
Re[8]: Какой код проще, лучше и почему
От: Silver_s Ниоткуда  
Дата: 07.04.10 13:30
Оценка: +1
Здравствуйте, Undying, Вы писали:

U>Объясни каким образом измельчение позволит из императивного кода сделать функциональный?

U>И в чем после этого код станет менее императивным?

Ну так чтобы на низком уровне остались лямбды и цепочки функций из Enumerable, то сомнительно что любой алгоритм можно было так реализовать не ухудшив читабельность.
Но все же когда одна функция заменяется на несколько более маленьких(каждая проще и понятнее исходной), которые последовательно вызываются и передают результат на вход следующей. Это декомпозиция в стиле функциональной.
При этом для функций/алгоритмов плохо поддающихся такой декомпозиции может возникнуть оверхед — некоторые излишества, которые могут только ухудшить ситуацию.
Вот была тема "Закон сохранения сложности", один из моментов там был примерно такой(очень грубо): Когда сложность не превышает какой-то критический уровень, лишние абстракции и какие-то трюки разделения обязанностей,декомпозиции могут только его усложнить и увеличить объем кода. А когда сложность непосильная, на такие трюки,декомпозиции можно пойти, оверхед тоже будет, но общий выигрыш уже положительный. Нелинейные эффекты. Так что для каждого случая индивидуально, в зависимости от конкретных метрик.
Тут кстати приводили небольшие излишества(черезмерное дробление), вроде каждая функция упрощается а общая сложность скорее увеличивается: http://www.rsdn.ru/forum/philosophy/3761260.1.aspx
Автор: sashar2
Дата: 04.04.10


А так чтобы большой и сложный императивный код (который нельзя уже упростить императивно) превращался в маленький и понятный чисто функциональный, таких примеров не так много(по крайней мере не большинство). Мне бы тоже было интересно посмотреть на их список.
Re[6]: Какой код проще, лучше и почему
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 07.04.10 13:58
Оценка:
Здравствуйте, Undying, Вы писали:

U>Я не требую реализовывать функционально конкретно этот алгоритм (это просто пример задачи со сложным промежуточным состоянием), но мне было бы интересно увидеть решение любой вашей задачи со сложным промежуточным состоянием в чисто функциональном стиле.


на haskell.
import Data.Time

type Pos = (Double,Double)

dist :: Pos -> Pos -> Double
dist (x1,y1) (x2,y2) = sqrt (x' * x' + y' * y')
                       where x' = x1 - x2
                             y' = y1 - y2

type ScheduleItem = (Int, DiffTime)
type Schedule =  [ScheduleItem]
type Track =     [(Pos, DiffTime)]
type Stations =  [Pos]

stations = [...]::Stations

getTrackSegmentWithinRadius :: Double -> Pos -> Track -> (Track, Track)
getTrackSegmentWithinRadius radius pos track =
    span (\(p,_) ->  dist pos p < radius) $ dropWhile (\(p,_) ->  dist pos p > radius) track


data StationPassStatus = InTime | Late | Early | NotPassed
                         deriving(Eq,Show)

getStationPassStatus :: DiffTime -> Track -> StationPassStatus
getStationPassStatus stationTime segment = 
    if null segment
        then NotPassed
        else
            --Super complex logic
            InTime 
            where times = map snd segment
                  min = minimum times
                  max = maximum times

getScheduleDiff :: Double -> Schedule -> Track -> [(Int, StationPassStatus)]
getScheduleDiff radius s track =
    drop 1 $
    map (\(_,a,b) -> (a,b)) $
    scanr (\(stationId, t) -> \(track,_,_) -> 
        let (seg, track') = getTrackSegmentWithinRadius radius (stations!!stationId) track
            status = getStationPassStatus t seg
        in (track', stationId, status)) (track, 0, NotPassed) s


не очень сложное состояние.

Вся передача состояния делается явно через параметры или неявно с помощью монад.
Re[7]: Какой код проще, лучше и почему
От: Undying Россия  
Дата: 08.04.10 04:37
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Здравствуйте, Undying, Вы писали:


U>>Я не требую реализовывать функционально конкретно этот алгоритм (это просто пример задачи со сложным промежуточным состоянием), но мне было бы интересно увидеть решение любой вашей задачи со сложным промежуточным состоянием в чисто функциональном стиле.


G>на haskell.


Haskell я к сожалению не понимаю, поэтому мне сложно что-то сказать по этому коду. А на шарп это легко переписывается?
Re[8]: Какой код проще, лучше и почему
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 08.04.10 05:31
Оценка: 4 (1)
Здравствуйте, Undying, Вы писали:

U>Здравствуйте, gandjustas, Вы писали:


G>>Здравствуйте, Undying, Вы писали:


U>>>Я не требую реализовывать функционально конкретно этот алгоритм (это просто пример задачи со сложным промежуточным состоянием), но мне было бы интересно увидеть решение любой вашей задачи со сложным промежуточным состоянием в чисто функциональном стиле.


G>>на haskell.


U>Haskell я к сожалению не понимаю, поэтому мне сложно что-то сказать по этому коду. А на шарп это легко переписывается?


Если забить на ленивость, то в 2 раза по объему больше будет.


public class Pos
{
    public double X { get; set; }
    public double Y { get; set; }
}

public class TrackItem
{
    public Pos Pos { get; set; }
    public DateTime Time { get; set; }
}

public enum StationPassStatus
{
    InTime, Late, Early, NotPassed
}

public static class EnumeraleExtensions
{
    public static Tuple<IEnumerable<T>, IEnumerable<T>> Span<T>(this IEnumerable<T> seq, Func<T, bool> pred)
    {
        return Tuple.Create(seq.TakeWhile(pred), seq.SkipWhile(pred));
    }

    public static IEnumerable<S> Scan<T, S>(this IEnumerable<T> seq, S seed, Func<T, S, S> f)
    {
        var state = seed; //Вот тут происходит передача состояния
        foreach (var e in seq)
        {
            yield return state;
            state = f(e, state);
        }
    }
}

public static class TrackExtensions
{
    public static double Dist(Pos p1, Pos p2)
    {
        var x = p1.X - p2.X;
        var y = p1.Y - p2.Y;
        return Math.Sqrt(x * x - y * y);
    }

    public static Tuple<IEnumerable<TrackItem>, IEnumerable<TrackItem>> 
            GetTrackSegmentWithinRadius(this IEnumerable<TrackItem> track, Pos pos, double radius)
    {
        return track.SkipWhile(t => Dist(t.Pos, pos) > radius)
                    .Span(t => Dist(t.Pos, pos) <= radius);
    }

    public static StationPassStatus GetStationPassStatus
        (this IEnumerable<TrackItem> track, DateTime stationTime)
    {
        if (!track.Any())
        {
            return StationPassStatus.NotPassed;
        }
        else
        {
            var times = track.Select(t => t.Time);
            var min = times.Min();
            var max = times.Max();

            //Супер-логика
            return StationPassStatus.InTime; 
        }

    }
}

class Program
{
    static Pos[] stations = new Pos[] { ... };

    static IEnumerable<Tuple<int, StationPassStatus>> GetScheduleDiff
        (IEnumerable<Tuple<int,DateTime>> schedule, 
         IEnumerable<TrackItem> track,
         double radius)
    {
        return schedule.Scan(new { Track = track, StationId = 0,  Status = StationPassStatus.NotPassed}, 
                            (st, s) =>
                            {
                                var r = s.Track.GetTrackSegmentWithinRadius(stations[st.Item1], radius);
                                var status = r.Item1.GetStationPassStatus(st.Item2);
                                return new { Track = r.Item2, StationId = st.Item1, Status = status };
                            })
                        .Select(t => Tuple.Create(t.StationId, t.Status))
                        .Skip(1);
    }
}
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.