2011-12-18 5 views

ответ

6

В Haskell, делая это для п-мерных списков, как эквивалентные опоры NumPy, требует достаточно передовой конструкции класса типов, но 1-мерный случай легко:

select :: [Bool] -> [a] -> [a] -> [a] 
select [] [] [] = [] 
select (True:bs) (x:xs) (_:ys) = x : select bs xs ys 
select (False:bs) (_:xs) (y:ys) = y : select bs xs ys 

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

Вот более простой, но менее очевидно, реализация для 1-мерных списков, переводя определение в документации NumPy (кредит Хоакин для указания его):

select :: [Bool] -> [a] -> [a] -> [a] 
select bs xs ys = zipWith3 select' bs xs ys 
    where select' True x _ = x 
     select' False _ y = y 

для достижения дела с двумя аргументов (возвращая все индексы, когда условие истинно; кредитного Рекс Керр для расшивки этого случая из), списка понимание может быть использовано:

trueIndices :: [Bool] -> [Int] 
trueIndices bs = [i | (i,True) <- zip [0..] bs] 

Это также может быть написано с существующие select, al хотя нет особого смысла:

trueIndices :: [Bool] -> [Int] 
trueIndices bs = catMaybes $ select bs (map Just [0..]) (repeat Nothing) 

А вот версия три-аргумент для п-мерных списков:

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-} 

class Select bs as where 
    select :: bs -> as -> as -> as 

instance Select Bool a where 
    select True x _ = x 
    select False _ y = y 

instance (Select bs as) => Select [bs] [as] where 
    select = zipWith3 select 

Вот пример:

GHCi> select [[True, False], [False, True]] [[0,1],[2,3]] [[4,5],[6,7]] 
[[0,5],[6,3]] 

Вы, вероятно, хотите использовать однако, на практике существует правильный тип n-мерного массива. Если вы просто хотите использовать select на п-мерный список для одного конкретного п, советы luqui (от замечания этого ответа) является предпочтительным:

На практике, вместо того, чтобы класс типов хака, я бы используйте (zipWith3.zipWith3.zipWith3) select' bs xs ys (для трехмерного случая).

(добавляя больше композиций zipWith3 в п увеличивается.)

+1

На практике вместо ручек классных я использую '(zipWith3.zipWith3.zipWith3), выберите 'bs xs ys' (для трехмерного случая). Если я не знаю количество измерений, когда пишу, тогда, как вы сказали, я бы использовал правильный абстрактный тип. – luqui

+0

Да, я полностью. Тем не менее, если вы действительно хотите реализовать 'numpy.where' в Haskell ... Спасибо, что сделали мне, что' zipWith3' будет работать здесь, кстати! Я отредактировал свой ответ соответственно и включил ваш комментарий. :) – ehird

+0

«Полностью согласен» и «заставляя меня осознать», конечно :) – ehird

5

В Python от numpy.where.__doc__:

If `x` and `y` are given and input arrays are 1-D, `where` is 
equivalent to:: 

    [xv if c else yv for (c,xv,yv) in zip(condition,x,y)] 
3

Есть два варианта использования для того, где; в одном случае у вас есть два массива, а в другом - только один.

В случае с двумя предметами, numpy.where(cond), вы получаете список индексов, в которых выполняется условие-массив. В Scala, как обычно

(cond, cond.indices).zipped.filter((c,_) => c)._2 

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

В случае три-пункта, numpy.where(cond,x,y), вы получаете либо x или y в зависимости от того, cond истинно (x) или ложь (y). В Scala

(cond, x, y).zipped.map((c,tx,ty) => if (c) tx else ty) 

выполняет ту же операцию (опять-таки менее компактную, но опять же, обычно не фундаментальную операцию). Обратите внимание, что в Scala вы можете легко иметь cond быть метод, который проверяет x и y и производит истинным или ложным, и тогда вы бы

(x, y).zipped.map((tx,ty) => if (c(tx,ty)) tx else ty) 

(хотя, как правило, даже если быть кратким вы бы назвать массивы xs и ys и отдельные элементы x и y).

Смежные вопросы