Я подарился загадка. Это состоит из 4 кубов, расположенных рядом. Поверхности каждого куба являются одним из четырех цветов.
Для решения загадки кубы должны ориентироваться так, чтобы вершины всех четырех кубов отличались, все их передние стороны отличаются, все их спины отличаются, и вся их нижняя часть отличаются. Левые и правые стороны не имеют значения.
Мое решение псевдокода было:
Я решил загадку с помощью реализации того псевдокода в F#, но не являюсь satisifed со способом, которым я сделал шаг 3:
let problemSpace =
seq { for c1 in cube1Orientations do
for c2 in cube2Orientations do
for c3 in cube3Orientations do
for c4 in cube4Orientations do
yield [c1; c2; c3; c4] }
Вышеупомянутый код очень конкретен, и только разрабатывает декартово произведение четырех последовательностей ориентаций. Я начал думать о способе записать это для n последовательностей ориентаций.
Я придумал (весь код с этого времени должен выполниться прекрасный в интерактивном F#):
// Used to just print the contents of a list.
let print =
Seq.fold (fun s i -> s + i.ToString()) "" >> printfn "%s"
// Computes the product of two sequences - kind of; the 2nd argument is weird.
let product (seq1:'a seq) (seq2:'a seq seq) =
seq { for item1 in seq1 do
for item2 in seq2 do
yield item1 |> Seq.singleton |> Seq.append item2 }
Функция продукта могла использоваться как так...
seq { yield Seq.empty }
|> product [ 'a'; 'b'; 'c' ]
|> product [ 'd'; 'e'; 'f' ]
|> product [ 'h'; 'i'; 'j' ]
|> Seq.iter print
... которые приводят к...
let productn (s:seq<#seq<'a>>) =
s |> Seq.fold (fun r s -> r |> product s) (seq { yield Seq.empty })
[ [ 'a'; 'b'; 'c' ]
[ 'd'; 'e'; 'f' ]
[ 'h'; 'i'; 'j' ] ]
|> productn
|> Seq.iter print
Это - точно использование, которое я хочу. productn имеет точно подпись, которую я хочу, и работает.
Однако использование продукта включает противную строку seq {приводят к Seq.empty}, и это неинтуитивно берет:
Второй аргумент не кажется корректным.
Тот странный интерфейс скрыт приятно productn, но все еще ворчит меня независимо.
Есть ли какие-либо более хорошие, более интуитивные способы в общем вычислить декартово произведение последовательностей n? Там кто-либо создается в функциях (или комбинация), которые делают это?
Используйте рекурсию: декартово произведение n списков {L1..LN} - это совокупность списков, которые вы получаете, когда добавляете каждый элемент в L1 в каждый подсписок в декартовом произведении списков {L2..LN}.
let rec cart1 LL =
match LL with
| [] -> Seq.singleton []
| L::Ls -> seq {for x in L do for xs in cart1 Ls -> x::xs}
Пример:
> cart1 [[1;2];[3;4;5];[6;7]] |> Seq.toList;;
val it : int list list =
[[1; 3; 6]; [1; 3; 7]; [1; 4; 6]; [1; 4; 7]; [1; 5; 6]; [1; 5; 7]; [2; 3; 6];
[2; 3; 7]; [2; 4; 6]; [2; 4; 7]; [2; 5; 6]; [2; 5; 7]]
Декартово произведение [1; 2] [3; 4; 5] и [6; 7] является объединением {1, добавленного к каждому списку в корзине [[3; 4; 5] ; [6; 7]]} и {2 добавлены к каждому списку в корзине [[3; 4; 5]; [6; 7]]}. Это второе предложение в операторе соответствия.
Вот первая попытка версии со списком. Думаю, это можно было бы немного поправить.
let rec cart nll =
let f0 n nll =
match nll with
| [] -> [[n]]
| _ -> List.map (fun nl->n::nl) nll
match nll with
| [] -> []
| h::t -> List.collect (fun n->f0 n (cart t)) h