рекурсивные возможные лямбда-выражения?

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

для установки объектов для насмешки, вероятно, необходимо использовать своего рода инверсию управления / шаблон внедрения зависимости, как в следующем псевдокоде:

class Bar
{
    private FooDataProvider _dataProvider;

    public instantiate(FooDataProvider dataProvider) {
        _dataProvider = dataProvider;
    }

    public getAllFoos() {
        // instead of calling Foo.GetAll() here, we are introducing an extra layer of abstraction
        return _dataProvider.GetAllFoos();
    }
}

class FooDataProvider
{
    public Foo[] GetAllFoos() {
        return Foo.GetAll();
    }
}

Теперь в Вашем модульном тесте, Вы создаете насмешку для FooDataProvider, который позволяет Вам называть метод GetAllFoos, не имея необходимость на самом деле поражать базу данных.

class BarTests
{
    public TestGetAllFoos() {
        // here we set up our mock FooDataProvider
        mockRepository = MockingFramework.new()
        mockFooDataProvider = mockRepository.CreateMockOfType(FooDataProvider);

        // create a new array of Foo objects
        testFooArray = new Foo[] {Foo.new(), Foo.new(), Foo.new()}

        // the next statement will cause testFooArray to be returned every time we call FooDAtaProvider.GetAllFoos,
        // instead of calling to the database and returning whatever is in there
        // ExpectCallTo and Returns are methods provided by our imaginary mocking framework
        ExpectCallTo(mockFooDataProvider.GetAllFoos).Returns(testFooArray)

        // now begins our actual unit test
        testBar = new Bar(mockFooDataProvider)
        baz = testBar.GetAllFoos()

        // baz should now equal the testFooArray object we created earlier
        Assert.AreEqual(3, baz.length)
    }
}

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

18
задан Eric Lifka 29 July 2009 в 21:54
поделиться

5 ответов

Возможно, вам стоит попробовать комбинатор Z , откуда этот пример:

>>> Z = lambda f: (lambda x: f(lambda *args: x(x)(*args)))(lambda x: f(lambda *args: x(x)(*args)))
>>> fact = lambda f: lambda x: 1 if x == 0 else x * f(x-1)
>>> Z(fact)(5)
120
8
ответ дан 30 November 2019 в 07:28
поделиться

Прежде всего, в рекурсивных лямбда-выражениях нет необходимости. Как вы сами указываете, чтобы лямбда-выражение вызывало само себя, у него должно быть имя. Но лямбда-выражения - это не что иное, как анонимные функции. Итак, если вы дадите лямбда-выражению имя, оно перестанет быть лямбда-выражением, а будет функцией.

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

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

add = lambda a, b: a > 0 and (1 + add(a-1, b)) or b

Которая решает эту проблему. Однако ваш первый def - правильный способ сделать это.

6
ответ дан 30 November 2019 в 07:28
поделиться
add = lambda a, b: b if a <= 0 else 1 + add(a - 1, b)
3
ответ дан 30 November 2019 в 07:28
поделиться

Вам нужен комбинатор Y или какой-нибудь другой комбинатор с фиксированной точкой .

Вот пример реализации в виде лямбда-выражения Python:

Y = lambda g: (lambda f: g(lambda arg: f(f)(arg))) (lambda f: g(lambda arg: f(f)(arg)))

Используйте его так:

factorial = Y(lambda f: (lambda num: num and num * f(num - 1) or 1))

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

Обратите внимание, что это действительно сложно для вашей функции add (), потому что комбинатор Y поддерживает передачу только одного аргумента. Вы можете получить больше аргументов, каррировав - но я оставлю это в качестве упражнения для читателя. : -)

0
ответ дан 30 November 2019 в 07:28
поделиться

Может быть, вам нужен комбинатор Y?

Изменить - сделать это комбинатором Z (я не понимал, что комбинаторы Y больше для вызова по имени)

Используя определение комбинатора Z из Википедия

>>> Z = lambda f: (lambda x: f(lambda *args: x(x)(*args)))(lambda x: f(lambda *args: x(x)(*args)))

Используя это, вы можете затем определить add как полностью анонимную функцию (т.е. без ссылки на ее имя в ее определении)

>>> add = Z(lambda f: lambda a, b: b if a <= 0 else 1 + f(a - 1, b))
>>> add(1, 1)
2
>>> add(1, 5)
6
19
ответ дан 30 November 2019 в 07:28
поделиться
Другие вопросы по тегам:

Похожие вопросы: