2016-09-25 2 views
2

Извините за заголовок gore (если вы можете предложить лучше, пожалуйста). Но моя проблема в том, что я не совсем понимаю, как заставить этот блок работать. У меня есть код, который возвращает позицию 5 в списке списков. Как например:Назначьте возвращаемое значение двух функций, двум переменным, в блоке do

findFive :: [[Int]] -> (Int, Int) 
findFive rs = do 
    x <- xPos rs 0 
    y <- yPos rs 0 
    return ((x,y)) 

xPos :: [[Int]] -> Int -> Int 
xPos (rs:[[]]) n    = n 
xPos (rs:rss) n | elem 5 rs = n 
       | otherwise = xPos rss (n+1) 

yPos :: [[Int]] -> Int -> Int 
yPos (rs:[[]]) n    = n 
yPos (rs:rss) n | elem 5 rs = n 
       | otherwise = yPos rss (n+1) 

Я

Но я не могу использовать мой сделать блок таким образом. Я могу заставить его работать, делая

findFive :: [[Int]] -> (Int, Int) 
findFive xs = ((xPos xs 0), (yPos (transpose (xs)) 0)) 

Но это выглядит как-то уродливо.

Кроме того, есть ли способ заставить это работать, не отправив 0 в xPos и yPos?

+0

XPOS и YPos идентичны, за исключением названия. Это совершенно не нужно и избегает всей функции. Когда вы получаете кодирование с копией и вставкой, почти всегда есть лучший способ. –

+0

@ Code-Apprentice Да. Я заметил это, вместо этого замените его на одну функцию 'pos'. И вызовы транспонируются в списке, который я отправляю для позиции Y –

+0

Один небольшой комментарий ... в '(yPos (transpose (xs))' вам не нужны скобки вокруг 'xs'. Помните, что в Haskell приложение-функция это простое пространство. Круглые скобки используются для указания приоритета оператора. Это не похоже на использование круглых скобок на других языках при вызове функции. Возможно, вы уже знаете это. Я просто хотел указать на будущих читателей. –

ответ

8

Почему do? Там нет монад. let..in хватает:

findFive :: [[Int]] -> (Int, Int) 
findFive rs = let 
    x = xPos rs 0 
    y = yPos rs 0 
    in (x,y) 

В качестве альтернативы, используйте where:

findFive :: [[Int]] -> (Int, Int) 
findFive rs = (x, y) 
    where 
    x = xPos rs 0 
    y = yPos rs 0 
+0

Спасибо! мы только недавно начали изучать монады. –

0

Вы не можете использовать do блокировать этот путь, потому что в do блоков вы должны (1) имена связываются с «содержанием» монадических значений и (2) вернуть значение, завернутое в тот же монадический тип, что и в (1). В этом случае монадическим типом будет список. Уместно возвращать список пар (строки, столбца), потому что это автоматически обрабатывает как случаи не нахождения числа, так и нахождения его несколько раз. Таким образом, мы могли бы сделать что-то вроде

import Control.Monad 

findFive :: 
    [[Int]] -> --^a matrix of numbers. 
    [(Int, Int)] --^the (zero-indexed) rows and columns of the number 
       --^@[email protected], if any (otherwise empty list). 
findFive xss = 
    do 
    (xs, rowIdx) <- xss `zip` [0 ..] 
    (x, colIdx) <- xs `zip` [0 ..] 
    guard $ x == 5 

    return (rowIdx, colIdx) 

input :: [[Int]] 
input = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] 

-- Should print "row: 1, col: 1". 
main :: IO() 
main = 
    forM_ (findFive input) $ \(rowIdx, colIdx) -> 
    do 
     putStr "row: " 
     putStr $ show rowIdx 
     putStr ", col: " 
     print colIdx 
0

Скажем pos определяются следующим образом:

pos :: Eq => a   -- A value to find 
      -> [[a]]  -- A 2d matrix 
      -> Maybe Int -- Index of first row containing the value, if any 
pos k rows = pos' rows 0 
      where pos' [] _ = Nothing 
        pos' (x:xs) n | elem k x = n 
           | otherwise = pos' xs (n+1) 

Есть несколько изменений здесь:

  1. Он будет работать для списков любого типа, на которых определяется равенство, а не только Int.
  2. Это обобщается, чтобы найти какое-либо значение k :: a, а не только 5.
  3. Он может иметь дело с отказом найти любой строка, содержащая k.

С этим определением, мы могли бы определить findFive, как

findFive :: [[Int]] -> (Maybe Int, Maybe Int) 
findFive xs = (pos 5 xs, pos 5 (transpose xs)) 

Control.Lens Используя, вы можете вынесем функцию pos 5 так, что ему нужно только записать один раз. Вспомните over both как версию map для пар вместо списков.

import Control.Lens 
findFive xs = over both (pos 5) (xs, transpose xs) 

Используя Control.Arrow, вы можете вынесем аргумент xsтак что это нужно только записать один раз.

import Control.Lens 
import Control.Arrow 
findFive xs = over both (pos 5) ((id &&& transpose) xs) 
-- id &&& transpose = \x -> (id x, transpose x) 

После того, как вы сделали это, вы можете легко написать findFive в точке свободного стиля, сочинением over both (pos 5) и id &&& transpose:

findFive = over both (pos 5) . (id &&& transpose) 
Смежные вопросы