Pythonic способ генерировать множество разделов? [Дубликат]

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

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

Рассмотрим пример. Вы называете молочника и заказываете молоко. Когда это произойдет, вы хотите положить его в свой кофе. Вы не можете положить молоко в свой кофе прямо сейчас, потому что его еще нет. Вы должны подождать, пока это произойдет, прежде чем положить его в свой кофе. Другими словами, следующее не будет работать:

var milk = order_milk();
put_in_coffee(milk);

Поскольку JS не знает, что ему нужно дождаться окончания order_milk, прежде чем он выполнит put_in_coffee. Другими словами, он не знает, что order_milk является асинхронным - это то, что не приведет к молоку до некоторого будущего времени. JS и другие декларативные языки, выполняйте один оператор за другим, не ожидая.

Классический подход JS к этой проблеме, используя тот факт, что JS поддерживает функции как объекты первого класса, которые могут быть переданы, заключается в передаче функции в качестве параметра для асинхронного запроса, который затем будет вызываться, когда он будет выполнять свою задачу в будущем. Это подход «обратного вызова». Это выглядит так:

order_milk(put_in_coffee);

order_milk запускает, заказывает молоко, тогда, когда и только когда он прибывает, он вызывает put_in_coffee.

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

order_milk(function(milk) { put_in_coffee(milk, drink_coffee); }

, где я перехожу к put_in_coffee как к молоку, чтобы положить в него, так и к действию (drink_coffee), чтобы выполнить как только молоко был введен. Такой код становится трудно писать, читать и отлаживать.

В этом случае мы могли бы переписать код в вопросе как:

var answer;
$.ajax('/foo.json') . done(function(response) {
  callback(response.data);
});

function callback(data) {
  console.log(data);
}

Enter обещает

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

В случае нашего молока и кофе мы создаем order_milk, чтобы вернуть обещание о прибытии молока, затем укажите put_in_coffee как действие then следующим образом:

order_milk() . then(put_in_coffee)

. Одно из преимуществ этого заключается в том, что мы можем объединить их вместе для создания последовательностей будущие вхождения («цепочка»):

order_milk() . then(put_in_coffee) . then(drink_coffee)

Давайте применим обещания к вашей конкретной проблеме. Мы завершим нашу логику запроса внутри функции, которая возвращает обещание:

function get_data() {
  return $.ajax('/foo.json');
}

На самом деле, все, что мы сделали, добавлено к return к вызову $.ajax. Это работает, потому что jQuery $.ajax уже возвращает вид обетоподобной вещи. (На практике, не вдаваясь в подробности, мы предпочли бы обернуть этот вызов, чтобы вернуть реальное обещание, или использовать некоторую альтернативу $.ajax, которая делает это.) Теперь, если мы хотим загрузить файл и дождаться его завершите, а затем сделайте что-нибудь, мы можем просто сказать

get_data() . then(do_something)

, например,

get_data() . 
  then(function(data) { console.log(data); });

. При использовании обещаний мы заканчиваем передачу множества функций в then, поэтому часто полезно использовать более компактные функции стрелок в стиле ES6:

get_data() . 
  then(data => console.log(data));

Ключевое слово async

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

a();
b();

, но если a является асинхронным, с обещаниями мы должны написать

a() . then(b);

Выше, мы сказали: «JS не имеет никакого способа узнать что ему нужно дождаться завершения первого вызова, прежде чем он выполнит второй ». Было бы неплохо, если бы можно было сказать JS? Оказывается, существует ключевое слово await, используемое внутри специального типа функции, называемого функцией «async». Эта функция является частью предстоящей версии ES, но уже доступна в транспилерах, таких как Babel, с учетом правильных настроек. Это позволяет нам просто написать

async function morning_routine() {
  var milk   = await order_milk();
  var coffee = await put_in_coffee(milk);
  await drink(coffee);
}

. В вашем случае вы могли бы написать что-то вроде

async function foo() {
  data = await get_data();
  console.log(data);
}
24
задан alexis 8 May 2015 в 23:28
поделиться

2 ответа

Поскольку этот приятный вопрос был возвращен к жизни, вот свежий ответ.

Проблема рекурсивно решается: если у вас уже есть раздел n-1 элементов , как вы используете его для разделения элементов n ? Либо поместите элемент n в один из существующих подмножеств, либо добавьте его как новое одноэлементное подмножество. Это все, что нужно; itertools:

def partition(collection):
    if len(collection) == 1:
        yield [ collection ]
        return

    first = collection[0]
    for smaller in partition(collection[1:]):
        # insert `first` in each of the subpartition's subsets
        for n, subset in enumerate(smaller):
            yield smaller[:n] + [[ first ] + subset]  + smaller[n+1:]
        # put `first` in its own subset 
        yield [ [ first ] ] + smaller


something = list(range(1,5))

for n, p in enumerate(partition(something), 1):
    print(n, sorted(p))

Вывод:

1 [[1, 2, 3, 4]]
2 [[1], [2, 3, 4]]
3 [[1, 2], [3, 4]]
4 [[1, 3, 4], [2]]
5 [[1], [2], [3, 4]]
6 [[1, 2, 3], [4]]
7 [[1, 4], [2, 3]]
8 [[1], [2, 3], [4]]
9 [[1, 3], [2, 4]]
10 [[1, 2, 4], [3]]
11 [[1], [2, 4], [3]]
12 [[1, 2], [3], [4]]
13 [[1, 3], [2], [4]]
14 [[1, 4], [2], [3]]
15 [[1], [2], [3], [4]]
: itertools: itertools: нет itertools, нет наборов, нет повторных выходов и всего только n
33
ответ дан alexis 27 August 2018 в 05:32
поделиться

В отличие от моих комментариев, я не смог быстро найти решение на основе itertools относительно быстро! Изменить: это уже не совсем так, у меня довольно короткое (но медленное и нечитаемое) решение с использованием itertools в значительной степени, см. Конец ответа. Это то, что я получил вместо этого:

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

Например, для списка длины 3 комбинации или разбиения являются (3), (2, 1), (1, 2) и (1, 1, 1). Поэтому мы возвращаем первые 3 элемента списка; первые 2, а затем следующий 1; первый 1, затем следующий 2 и первый 1, затем следующий 1, затем следующий 1.

Я получил код для целочисленного partioning из здесь . Однако функции разделов не возвращают все перестановки разделов (т. Е. Для 3 она просто вернет (3), (2, 1) и (1, 1, 1). Поэтому нам нужно вызвать itertools.permutations на каждом из Затем нам нужно удалить дубликаты - так же, как permutations([1, 2, 3]) - [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]], permutations([1, 1, 1]) - [[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]]. Легкий способ удаления дубликатов - это превратить каждый список кортежей в set.

Тогда все, что осталось, - это получение фрагментов списка для длин в кортеже. Например, f([1, 2, 3], [0, 0, 1, 2, 1, 0]) отправляется на [[0], [0, 1], [2, 1, 0]].

Мое определение этого:

def slice_by_lengths(lengths, the_list):
    for length in lengths:
        new = []
        for i in range(length):
            new.append(the_list.pop(0))
        yield new

Теперь мы просто соединим все:

def subgrups(my_list):
    partitions = partition(len(my_list))
    permed = []
    for each_partition in partitions:
        permed.append(set(itertools.permutations(each_partition, len(each_partition))))

    for each_tuple in itertools.chain(*permed):
        yield list(slice_by_lengths(each_tuple, deepcopy(my_list)))

>>> for i in subgrups(my_list):
        print(i)

[[1], [2], [3]]
[[1], [2, 3]]
[[1, 2], [3]]
[[1, 2, 3]]

Также вам нужно сделать import itertools и from copy import deepcopy в верхней части программы.

Изменить: ваш выходной результат не ясен. Я предположил, что вам нужна функция, которую я вам дал, но ваш вывод также содержит [[1,3],[2]], где элементы на выходе находятся в другом порядке, в отличие от остальной части вашего предлагаемого вывода ( Я взял на себя смелость предположить, что вы действительно хотите [[1, 2], [3]] не [[1, 2], 3]).

То есть, я полагаю, что y ou означало, что вывод был следующим:

[[1], [2], [3]]
[[1], [2, 3]]
[[1, 2], [3]]
[[1, 2, 3]]

Если на самом деле это было так:

[[1], [2], [3]]
[[1], [2, 3]]
[[1, 2], [3]]
[[1, 2, 3]]
[[1], [3], [2]]
[[1], [3, 2]]
[[1, 3], [2]]
[[1, 3, 2]]
[[2], [1], [3]]
[[2], [1, 3]]
[[2, 1], [3]]
[[2, 1, 3]]
[[2], [3], [1]]
[[2], [3, 1]]
[[2, 3], [1]]
[[2, 3, 1]]
[[3], [1], [2]]
[[3], [1, 2]]
[[3, 1], [2]]
[[3, 1, 2]]
[[3], [2], [1]]
[[3], [2, 1]]
[[3, 2], [1]]
[[3, 2, 1]]

Тогда вам просто нужно вызвать subgrups для каждого 3- длина перестановки исходного списка, например для каждой перестановки в itertools.permutations(my_list, len(my_list)).

Изменить: теперь, чтобы выполнить мое обещание короткого решения itertools. Предупреждение - оно может быть как нечитаемым, так и медленным.

Сначала мы заменим slice_by_lengths на это:

def sbl(lengths, the_list):
    for index, length in enumerate(lengths):
        total_so_far = sum(lengths[:index])
        yield the_list[total_so_far:total_so_far+length]

Тогда из этот ответ получим наш целочисленная функция секционирования:

def partition(number):
    return {(x,) + y for x in range(1, number) for y in partition(number-x)} | {(number,)}

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

for each_partition in partitions:
    permed.append(set(itertools.permutations(each_partition, len(each_partition))))

. Тем не менее, он намного медленнее, чем у нас раньше, поскольку он рекурсивный (и мы реализуем его в Python).

Затем мы просто разделим его:

def subgrups(my_list):
    for each_tuple in partition(len(my_list)):
        yield list(slice_by_lengths(each_tuple, deepcopy(my_list)))

Или менее читаемым, но без определений функций:

def subgrups(my_list):
    for each_tuple in (lambda p, f=lambda n, g:
                          {(x,) + y for x in range(1, n) for y in g(n-x, g)} | {(n,)}:
                              f(p, f))(len(my_list)):
        yield list(my_list[sum(each_tuple[:index]):sum(each_tuple[:index])+length] for index, length in enumerate(each_tuple))

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

(Функции, называемые subgrups, поскольку первоначально задан вопрос об обнаружении «всех подгрупп»)

10
ответ дан Community 27 August 2018 в 05:32
поделиться
Другие вопросы по тегам:

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