Так что отпиши баг-репорт, а сам используй void-функцию пока баг не поправят.
Но хочется сказать по пводу match-а и предупреждений. Все же так писать не хорошо. Лучше ставить вхождения матча с константными значениями перед вхождениями с открытым паттерном (т.е. с переменными, например BuffOut(n)). А вместо того чтобы делать проверки вроде "when n > 0" лучше добавлять вхождения проверяющие на "n < 0". Так что я бы написал твой код так:
using System;
using System.Console;
using Nemerle.Utility;
using System.Collections.Generic;
module Program
{
private variant ST
{
| Scan
| Lead
| Gold
| BuffOut {count : int;}
| GoldOut
| LeadOut {count : int;}
}
public SelectWithTrain[T](this seq : IEnumerable[T],
predicate : T -> bool,
train : int = 5
)
: IEnumerable[T]
{
def queue = Nemerle.Collections.Queue(train);
mutable state = ST.Scan();
foreach(elem in seq)
{
def loop(s : ST) : void
{
| Scan => loop(if(predicate(elem)) ST.Gold() else ST.Lead())
| Lead =>
when (queue.Count >= train)
_ = queue.Dequeue();
queue.Enqueue(elem);
state = ST.Scan()
| Gold => loop(ST.BuffOut(queue.Count))
| BuffOut(0) => loop(ST.GoldOut())
| BuffOut(n) when n < 0 => throw Exception($"Хреновое состояние BuffOut($n)!")
| BuffOut(n) =>
yield queue.Dequeue();
loop(ST.BuffOut(n-1))
| GoldOut =>
yield elem;
state = ST.LeadOut(train)
| LeadOut(0) => state = ST.Scan()
| LeadOut(n) when n < 0 => throw Exception($"Хреновое состояние LeadOut($n)!")
| LeadOut(n) =>
if (predicate(elem))
loop(ST.Gold())
else
{
yield elem;
state = ST.LeadOut(n-1)
}
}
loop(state)
}
}
Main() : void
{
def lst = [-1, 2, 3, -4, -5, -6, -7, -8, -9, -10, 11, -12, 13, 14, 15, -16];
def iter = lst.SelectWithTrain(_ > 0, 2);
// ожидаемый вывод [-1, 2, 3, -4, -5, -9, -10, 11, -12, 13, 14, 15, -16], но...
WriteLine($[x | x in iter]); // здесь возникает исключение, о котором ниже.
_ = ReadLine();
}
}
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Нижеследующий код выполняет, в двух словах, следующую работу: последовательность элементов преобразует в другую последовательность, которая состоит из элементов отвечающих некоторому условию и заданного кол-ва соседствующих с ними элементов.
Данный код компилируется без вопросов, с одним единственным предупреждением, касающимся матчинга (переписывание матчинга так, чтобы предупреждение исчезло ничего не меняет, так что к делу не относится.), но получившийся автомат не работает, выбрасывая исключение в рантайм.
using System;
using System.Console;
using Nemerle.Utility;
using System.Collections.Generic;
module Program
{
private variant ST
{
| Scan
| Lead
| Gold
| BuffOut {count : int;}
| GoldOut
| LeadOut {count : int;}
}
public SelectWithTrain[T](this seq : IEnumerable[T],
predicate : T -> bool,
train : int = 5
) : IEnumerable[T]
{
def queue = Nemerle.Collections.Queue(train);
mutable state = ST.Scan();
foreach(elem in seq)
{
def loop(s : ST)
{
| Scan => loop(if(predicate(elem)) ST.Gold() else ST.Lead())
| Lead => {
when (queue.Count >= train)
_ = queue.Dequeue();
queue.Enqueue(elem);
ST.Scan()
}
| Gold => loop(ST.BuffOut(queue.Count))
| BuffOut(n) when n > 0 => {
yield queue.Dequeue();
loop(ST.BuffOut(n-1))
}
| BuffOut(0) => loop(ST.GoldOut())
| GoldOut => {
yield elem;
ST.LeadOut(train)
}
| LeadOut(n) when n > 0 => {
if (predicate(elem))
loop(ST.Gold())
else
{
yield elem;
ST.LeadOut(n-1)
}
}
| LeadOut(0) => ST.Scan()
}
state = loop(state)
}
}
Main() : void
{
def lst = [-1, 2, 3, -4, -5, -6, -7, -8, -9, -10, 11, -12, 13, 14, 15, -16];
def iter = lst.SelectWithTrain(_ > 0, 2);
// ожидаемый вывод [-1, 2, 3, -4, -5, -9, -10, 11, -12, 13, 14, 15, -16], но...
WriteLine($[x | x in iter]); // здесь возникает исключение, о котором ниже.
}
}
А вот и исключение:
System.InvalidProgramException was unhandled
Message: Common Language Runtime detected an invalid program.
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll
Здравствуйте, Иванков Дмитрий, Вы писали:
ИД>Баг, должно быть сообщение о том, что yield нельзя использовать в локальных функциях.
Исключение конечно всегда баг, но сообщение тут не причем.
yield допустим в локальных функциях. Считаетя, что локальные функции заменяют циклы.
Думаю, что проблема в данном случае в том, что компилятор не верно обрабатывает фукнцию с yield которая при этом возвращает еще и некоторое значение. Посторить ДКА при этом несколько сложнее нежели в случае с void-фунцией.
Попробовал переписать код так чтобы функция ничего не возвращала... и все заработало. Вот код:
using System;
using System.Console;
using Nemerle.Utility;
using System.Collections.Generic;
module Program
{
private variant ST
{
| Scan
| Lead
| Gold
| BuffOut {count : int;}
| GoldOut
| LeadOut {count : int;}
}
public SelectWithTrain[T](this seq : IEnumerable[T],
predicate : T -> bool,
train : int = 5
) : IEnumerable[T]
{
def queue = Nemerle.Collections.Queue(train);
mutable state = ST.Scan();
foreach(elem in seq)
{
def loop(s : ST) : void
{
| Scan => loop(if(predicate(elem)) ST.Gold() else ST.Lead())
| Lead => {
when (queue.Count >= train)
_ = queue.Dequeue();
queue.Enqueue(elem);
state = ST.Scan()
}
| Gold => loop(ST.BuffOut(queue.Count))
| BuffOut(0) => loop(ST.GoldOut())
| BuffOut(n) when n > 0 => {
yield queue.Dequeue();
loop(ST.BuffOut(n-1))
}
| GoldOut => {
yield elem;
state = ST.LeadOut(train)
}
| LeadOut(0) => state = ST.Scan()
| LeadOut(n) when n > 0 => {
if (predicate(elem))
loop(ST.Gold())
else
{
yield elem;
state = ST.LeadOut(n-1)
}
}
| _ => throw Exception("Вообще такие вещи нужно обрабатвать по человечески!")
}
loop(state)
}
}
Main() : void
{
def lst = [-1, 2, 3, -4, -5, -6, -7, -8, -9, -10, 11, -12, 13, 14, 15, -16];
def iter = lst.SelectWithTrain(_ > 0, 2);
// ожидаемый вывод [-1, 2, 3, -4, -5, -9, -10, 11, -12, 13, 14, 15, -16], но...
WriteLine($[x | x in iter]); // здесь возникает исключение, о котором ниже.
_ = ReadLine();
}
}
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, Иванков Дмитрий, Вы писали:
ИД>>Баг, должно быть сообщение о том, что yield нельзя использовать в локальных функциях.
VD>Исключение конечно всегда баг, но сообщение тут не причем.
VD>yield допустим в локальных функциях. Считаетя, что локальные функции заменяют циклы.
Ок, пусть допустим. Но не работает ни в одном из следующих случаев:
1) вызов происходит >= 2 раза
2) вызов рекурсивный
3) вызов из другой лок. ф-ии
4) вызов из другой лок. ф-ии объявленной через def ... and ...
5) тип результата не void
Т.е. работает только в случае если можно убрать объявление ф-ии и заменить единственный вызов ее телом.
Мне кажется компиллятор должен честно сказать, что пока что yield в локальных функциях не поддерживается.
Здравствуйте, Иванков Дмитрий, Вы писали:
ИД>Здравствуйте, VladD2, Вы писали:
VD>>yield допустим в локальных функциях. Считаетя, что локальные функции заменяют циклы.
ИД>Но не работает ни в одном из следующих случаев: ИД>2) вызов рекурсивный