Как раз тогда, когда stackoverflow является честным и разумным? - программирование
Подтвердить что ты не робот

Как раз тогда, когда stackoverflow является честным и разумным?

Обновленный код

Для исправления ошибки отфильтрованного Interminable, следующий код обновляется и объединяется в оригинал:

public static bool IsInfinity(this IEnumerable x) {
    var it=
        x as Infinity??((Func<object>)(() => {
            var info=x.GetType().GetField("source", bindingAttr);
            return null!=info?info.GetValue(x):x;
        }))();

    return it is Infinity;
}

bindingAttr объявляется константой.


  • Резюме

    Я пытаюсь реализовать бесконечное перечислимое, но встречающееся что-то кажется нелогичным и временно заканчивается. Мне нужно какое-то направление для завершения кода, став семантическим, логичным и разумным дизайном.

  • Вся история

    Я задал вопрос несколько часов назад:

    Является бесконечным перечислимым все еще "перечислимым" ,

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

    большая проблема - это просто для представления бесконечного перечислимого, но перечисление на нем на самом деле не имеет никакого смысла, поскольку нет никаких реальных элементов.

    Итак, помимо предоставления фиктивных элементов для перечисления, есть четыре варианта, которые я могу себе представить, а три приводят к StackOverflowException.

    • Бросьте InvalidOperationException, как только он будет перечислить.

      public IEnumerator<T> GetEnumerator() {
          for(var message="Attempted to enumerate an infinite enumerable"; ; )
              throw new InvalidOperationException(message);
      }
      
    • и 3. технически эквивалентны, пусть переполнение стека происходит при переполнении действительно.

      public IEnumerator<T> GetEnumerator() {
          foreach(var x in this)
              yield return x;
      }
      
      public IEnumerator<T> GetEnumerator() {
          return this.GetEnumerator();
      }
      
    • (описано в 2)

    • Не ждите, когда это произойдет, просто бросьте StackOverflowException.

      public IEnumerator<T> GetEnumerator() {
          throw new StackOverflowException("... ");
      }
      

Сложные вещи:

Если применяется option 1, то есть перечислять на этом перечислимом, становится недопустимой операцией. Разве не странно говорить, что эта лампа не используется для освещения (хотя это верно в моем случае).

Если применяется option 2 или option 3, то есть мы планировали переполнение стека. Действительно ли это как название, только когда stackoverflow является справедливым и разумным? Совершенно логично и разумно?

Последний выбор option 4. Тем не менее, стек фактически не переполняется, поскольку мы предотвратили его, выбросив поддельный StackOverflowException. Это напоминает мне, что, когда Том Круз играет Джон Андертон, он сказал, что: "Но это не упало, ты его поймал. Тот факт, что ты предотвратил это, не меняет того факта, что это произойдет".

Некоторые хорошие способы избежать нелогичных проблем?


Код компилируемый и проверяемый, обратите внимание, что перед компиляцией необходимо определить один из OPTION_1 до OPTION_4 shoule.

  • Простой тест

    var objects=new object[] { };
    Debug.Print("{0}", objects.IsInfinity());
    var infObjects=objects.AsInterminable();
    Debug.Print("{0}", infObjects.IsInfinity());
    
  • <сильные > Классы

    using System.Collections.Generic;
    using System.Collections;
    using System;
    
    public static partial class Interminable /* extensions */ {
        public static Interminable<T> AsInterminable<T>(this IEnumerable<T> x) {
            return Infinity.OfType<T>();
        }
    
        public static Infinity AsInterminable(this IEnumerable x) {
            return Infinity.OfType<object>();
        }
    
        public static bool IsInfinity(this IEnumerable x) {
            var it=
                x as Infinity??((Func<object>)(() => {
                    var info=x.GetType().GetField("source", bindingAttr);
                    return null!=info?info.GetValue(x):x;
                }))();
    
            return it is Infinity;
        }
    
        const BindingFlags bindingAttr=
            BindingFlags.Instance|BindingFlags.NonPublic;
    }
    
    public abstract partial class Interminable<T>: Infinity, IEnumerable<T> {
        IEnumerator IEnumerable.GetEnumerator() {
            return this.GetEnumerator();
        }
    
    #if OPTION_1
        public IEnumerator<T> GetEnumerator() {
            for(var message="Attempted to enumerate an infinite enumerable"; ; )
                throw new InvalidOperationException(message);
        }
    #endif
    
    #if OPTION_2
        public IEnumerator<T> GetEnumerator() {
            foreach(var x in this)
                yield return x;
        }
    #endif
    
    #if OPTION_3
        public IEnumerator<T> GetEnumerator() {
            return this.GetEnumerator();
        }
    #endif
    
    #if OPTION_4
        public IEnumerator<T> GetEnumerator() {
            throw new StackOverflowException("... ");
        }
    #endif
    
        public Infinity LongCount<U>(
            Func<U, bool> predicate=default(Func<U, bool>)) {
            return this;
        }
    
        public Infinity Count<U>(
            Func<U, bool> predicate=default(Func<U, bool>)) {
            return this;
        }
    
        public Infinity LongCount(
            Func<T, bool> predicate=default(Func<T, bool>)) {
            return this;
        }
    
        public Infinity Count(
            Func<T, bool> predicate=default(Func<T, bool>)) {
            return this;
        }
    }
    
    public abstract partial class Infinity: IFormatProvider, ICustomFormatter {
        partial class Instance<T>: Interminable<T> {
            public static readonly Interminable<T> instance=new Instance<T>();
        }
    
        object IFormatProvider.GetFormat(Type formatType) {
            return typeof(ICustomFormatter)!=formatType?null:this;
        }
    
        String ICustomFormatter.Format(
            String format, object arg, IFormatProvider formatProvider) {
            return "Infinity";
        }
    
        public override String ToString() {
            return String.Format(this, "{0}", this);
        }
    
        public static Interminable<T> OfType<T>() {
            return Instance<T>.instance;
        }
    }
    
4b9b3361

Ответ 1

Как упоминалось в другом сообщении, которое вы связали, бесконечное перечисление имеет смысл перечислить С# и есть огромное количество примеров реального мира, где люди пишут перечисления, которые просто никогда не заканчиваются (первое, что отвлекает меня является генератором случайных чисел).

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

Более конкретный ответ на ваш текущий вопрос: НЕ КОГДА-ЛИБО НИКОГДА не вызывать переполнение реального стека. Это самая неприятная вещь, которую вы можете сделать для пользователей вашего кода. Его невозможно поймать и немедленно прекратит ваш процесс (возможно, единственным исключением является то, что вы работаете внутри подключенного отладчика инструментов).

Если вообще, я бы использовал NotSupportedException, который используется в других местах, чтобы сигнализировать, что какой-то класс не поддерживает функцию (например, ICollection может бросить это в Remove(), если они доступны только для чтения).

Ответ 2

public IEnumerator<T> GetEnumerator()
{
    while (true)
        yield return default(T);
}

Это создаст бесконечный перечислитель - foreach на нем никогда не закончится и будет продолжать выдавать значение по умолчанию.

Обратите внимание, что вы не сможете определить IsInfinity() способ, которым вы написали код. Это потому, что new Infinity().Where(o => o == /*do any kind of comparison*/) будет по-прежнему бесконечным, но будет иметь другой тип.

Ответ 3

Если я правильно понимаю, бесконечное - это путаное слово здесь. Я думаю, вам нужна монада, которая либо перечислима, либо нет. Но пусть навсегда останется безграничным.

Я не могу придумать хороший способ реализации этого в С#. Все пути, которые это может быть реализовано, не интегрируются с генераторами С#.

С С# generator вы можете испускать только допустимые значения; поэтому нет способа указать, что это бесконечное перечислимое. Мне не нравится идея бросать исключения из генератора, чтобы указать, что она бесконечна; потому что, чтобы проверить, что это бесконечно, вам придется каждый раз пытаться поймать.

Если вам не нужно поддерживать генераторы, я вижу следующие параметры:

  • Внедрение счетного числа:

    public class InfiniteEnumerable<T>: IEnumerable<T> {
        private static InfiniteEnumerable<T> val;
    
        public static InfiniteEnumerable<T> Value {
            get {
                return val;
            }
        }
    
        public IEnumerator<T> GetEnumerator() {
            throw new InvalidOperationException(
                "This enumerable cannot be enumerated");
        }
    
        IEnumerator IEnumerable.GetEnumerator() {
            throw new InvalidOperationException(
                 "This enumerable cannot be enumerated");
        }
    }
    

    Использование образца:

    IEnumerable<int> enumerable=GetEnumerable();
    
    if(enumerable==InfiniteEnumerable<int>.Value) {
        // This is 'infinite' enumerable.
    }
    else {
        // enumerate it here.
    }
    
  • Внедрить Infinitable<T> wrapper:

    public class Infinitable<T>: IEnumerable<T> {
        private IEnumerable<T> enumerable;
        private bool isInfinite;
    
        public Infinitable(IEnumerable<T> enumerable) {
            this.enumerable=enumerable;
            this.isInfinite=false;
        }
    
        public Infinitable() {
            this.isInfinite=true;
        }
    
        public bool IsInfinite {
            get {
                return isInfinite;
            }
        }
    
        public IEnumerator<T> GetEnumerator() {
            if(isInfinite) {
                throw new InvalidOperationException(
                    "The enumerable cannot be enumerated");
            }
    
            return this.enumerable.GetEnumerator();
        }
    
        IEnumerator IEnumerable.GetEnumerator() {
            if(isInfinite) {
                throw new InvalidOperationException(
                     "The enumerable cannot be enumerated");
            }
    
            return this.enumerable.GetEnumerator();
        }
    }
    

    Использование образца:

    Infinitable<int> enumerable=GetEnumerable();
    
    if(enumerable.IsInfinite) {
        // This is 'infinite' enumerable.
    }
    else {
        // enumerate it here.
        foreach(var i in enumerable) {
        }
    }
    

Ответ 4

Бесконечные последовательности могут быть совершенно итерабельными/перечислимыми. Естественные числа перечислимы, а также являются рациональными числами или цифрами PI. Бесконечность - это противоположность конечному, неперечислимому.

Варианты, которые вы предоставили, не представляют бесконечные последовательности. Существует бесконечно много разных бесконечных последовательностей, и вы можете видеть, что они различаются, итерации через них. С другой стороны, ваша идея состоит в том, чтобы иметь один синглтон, который идет вразрез с этим разнообразием.

Если у вас есть что-то, что невозможно перечислить (например, набор действительных чисел), вы просто не должны определять его как IEnumerable, поскольку он нарушает контракт. Если вы хотите различить конечные и бесконечные перечислимые последовательности, просто создайте новый интерфейс IInfiniteEnumerable : IEnumerable и отметьте с ним бесконечные последовательности.

Интерфейс, который отмечает бесконечные последовательности

public interface IInfiniteEnumerable<T> : IEnumerable<T> {
}

Обертка для преобразования существующих IEnumerable<T> в IInfiniteEnumerable<T> (IEnumerable легко создается с синтаксисом С# yield, но нам нужно преобразовать их в IInfiniteEnumerable)

public class InfiniteEnumerableWrapper<T> : IInfiniteEnumerable<T> {
    IEnumerable<T> _enumerable;

    public InfiniteEnumerableWrapper(IEnumerable<T> enumerable) {
        _enumerable = enumerable;
    }

    public IEnumerator<T> GetEnumerator() {
        return _enumerable.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator() {
        return _enumerable.GetEnumerator();
    }
}

Некоторые процедуры, поддерживающие бесконечность (например, вычисление длины последовательности)

//TryGetCount() returns null if the sequence is infinite
public static class EnumerableExtensions {
    public static int? TryGetCount<T>(this IEnumerable<T> sequence) {
        if (sequence is IInfiniteEnumerable<T>) {
            return null;
        } else {
            return sequence.Count();
        }
    }
}

Два примера последовательностей - последовательность конечного диапазона и бесконечная последовательность Фибоначчи.

public class Sequences {
    public static IEnumerable<int> GetIntegerRange(int start, int count) {
        return Enumerable.Range(start, count);
    }

    public static IInfiniteEnumerable<int> GetFibonacciSequence() {
        return new InfiniteEnumerableWrapper<int>(GetFibonacciSequenceInternal());
    }

    static IEnumerable<int> GetFibonacciSequenceInternal() {
        var p = 0;
        var q = 1;
        while (true) {
            yield return p;
            var newQ = p + q;
            p = q;
            q = newQ;
        }
    }
}

Тестовое приложение, которое генерирует случайные последовательности и пытается рассчитать их длины.

public class TestApp {
    public static void Main() {
        for (int i = 0; i < 20; i++) {
            IEnumerable<int> sequence = GetRandomSequence();
            Console.WriteLine(sequence.TryGetCount() ?? double.PositiveInfinity);
        }
        Console.ReadLine();
    }

    static Random _rng = new Random();
    //Randomly generates an finite or infinite sequence
    public static IEnumerable<int> GetRandomSequence() {
        int random = _rng.Next(5) * 10;
        if (random == 0) {
            return Sequences.GetFibonacciSequence();
        } else {
            return Sequences.GetIntegerRange(0, random);
        }
    }
}

Программа выводит что-то вроде этого:

20
40
20
10
20
10
20
Infinity
40
30
40
Infinity
Infinity
40
40
30
20
30
40
30