2010-06-11 2 views
1

Я пытаюсь написать простую программу в Haskell, которая может определить индекс массы тела человека.Программа индекса массы тела в haskell

Вот что я написал:

type Height = Float 
type Weight = Float 
type PeopleStats = [(String, Height, Weight)] 

и ...

bmi :: Height -> Weight -> Float 
bmi heightCm weightKg = weightKg/(heightCm)^2 

healthy :: Height -> Weight -> Bool 
healthy heightCm weightKg | 25 > index && 18 < index = True 
          | otherwise    = False 
    where index = bmi heightCm weightKg 

До сих пор функция «здоровой» может вычислить кого-то ИМТ, а функция «healthyPeople» возвращает логическое значение заявление, определяющее, находится ли ИМТ лица в пределах, которые считаются нормальными для здорового человека.

Я хочу написать функцию под названием «здоровые люди».

healthyPeople :: PeopleStats -> [String] 

Эта функция должна принимать список PeopleStats и возвращает список имен (струнные) людей, которые считаются «здоровыми» от «здоровой» функции.

Например:

Если я входной [("Lee", 65, 185), ("Wang", 170, 100), ("Tsu", 160, 120)] я получить список имен людей, чей индекс массы тела возвращает истинную форму булеву функцию в «здоровой».

Пожалуйста, помогите !!!!

+1

Вы, кажется, потеряли некоторые отступы в определении «здоровый». Высота и вес для примера также выглядят необычно. –

ответ

0

Во-первых, обратите внимание, что ваше определение ИМТ является ошибочной - вы должны преобразовать сантиметры в метры:

bmi heightCm weightKg = weightKg/(heightCm/100)^2 

С этим фиксирована, я придумал следующее:

healthyPeople :: PeopleStats -> [String] 
healthyPeople [] = [] 
healthyPeople ((name, height, weight):xs) | healthy height weight = name : healthyPeople xs 
              | otherwise = healthyPeople xs 

Этот довольно прямолинейно. Он использует list-based recursion для рекурсии над всеми элементами списка, и он использует охранники аналогично тому, как вы использовали их в здоровой функции для переключения поведения на основе того, является ли человек во главе списка исцеленным или нет. Если они здоровы, их имя равно concatted with результат обработки остальной части списка.

В следующий раз, вы должны попытаться решить проблему самостоятельно и затем обратиться за помощью (и показать, что вы попробовали). Вы узнаете гораздо больше!

+0

Если вы добавили к списку, вы должны использовать 'x:', а не '[x] ++', чтобы избежать конструирования и затем игнорировать весь дополнительный список. –

+0

@Antal Спасибо. Прошло довольно много времени, так как я коснулся Haskell, '[x] ++' не сидел прямо со мной, но я не мог понять, почему :) – Chris

0

Давайте подумаем о том, что вы хотите сделать. У вас есть список, и вы хотите (a) выбрать только определенные элементы из списка и (b) сделать что-то для каждого элемента списка. Это Хаскелл, давайте выражаем это в типах. Первое, что вам нужно - ну, он должен будет взять список [a] и способ проверить, хорош ли каждый элемент. Как это можно проверить? Ну, это должна быть функция a -> Bool. И это должно вернуть нам меньший список. Другими словами, что-то вроде [a] -> (a -> Bool) -> [a]. Затем мы хотим взять наш список и сделать что-то для каждого элемента. Другими словами, нам понадобится список [a] и функция a -> b. Таким образом, нам нужно что-то типа [a] -> (a -> b) -> [b]. Теперь, когда у нас есть типы, мы золотые: мы можем использовать Hoogle для их поиска. I высоко, настоятельно рекомендуем регулярно использовать Hoogle; это поисковая система Haskell, которая ищет как типы - уникальные удивительные имена частей и функций/типов данных/типов/модулей/модулей. first function, как выясняется, является вторым результатом для запроса: filter :: (a -> Bool) -> [a] -> [a].Это принимает функцию и список и возвращает только те элементы списка, для которых функция истинна. second function - это первый результат, map :: (a -> b) -> [a] -> [b], который вызывает данную функцию для каждого элемента данного списка и возвращает список результатов. Обратите внимание, что аргументы сначала имеют функцию, а не список; это более естественно, как вы вскоре увидите.

Мы хотим поставить эти два вместе healthyPeople:

healthyPeople :: PeopleStats -> [String] 
healthyPeople sts = map (\(n,_,_) -> n) $ filter (\(_,h,w) -> healthy h w) sts 

Это делает то, что вы хотите. $ является функциональным приложением, но эффективно группирует правую часть из-за его приоритета; это позволяет нам исключать круглые скобки. Здесь мы видим, почему приятно, что map выполняет свою функцию; мы передаем ему функцию извлечения имени ((n,_,_) - это шаблон, который будет соответствовать тройке и присвойте n его первый элемент, игнорируя остальные два), а затем (через $) отфильтрованный список.

Это хорошо, но не так, как я на самом деле его написал. Поскольку sts является последним параметром функции и ее телу, это не нужно. По правде говоря, все функции в Haskell принимают только один аргумент; это означает, что если вы не пройдете достаточно аргументов, вы получите функцию, которая ожидает отсутствующих аргументов и возвращает результат. С помощью оператора-функции состава ., это дает нам

healthyPeople :: PeopleStats -> [String] 
healthyPeople = map (\(n,_,_) -> n) . filter (\(_,h,w) -> healthy h w) 

И это, наверное, как я пишу это! Вы часто будете использовать map и filter; они являются настоящими рабочими лошадками в функциональном программировании.

Существует еще один идиоматический способ, которым вы можете написать healthyPeople; Вы можете использовать список понимание, следующим образом:.

healthyPeople :: PeopleStats -> [String] 
healthyPeople stats = [n | (n,h,w) <- stats, healthy h w] 

Это читается как «построить список каждый n таким образом, что (n,h,w) является элементом stats и healthy h w верно Если какой-либо из шаблона соответствует или предикаты сбой (у вас может быть более одного, каждый из которых вам не нужен), этот элемент пропускается, в противном случае выполняется левая сторона |.Это фактически другой способ записи версии map/filter.


Редактирование 1: Как и многие другие, ваши устройства выключены в bmi; вы должны иметь heightCm/100.Кроме того, ваша healthy функция имеет код, эквивалентный

f x | cond  = True 
    | otherwise = False 

Это эквивалентно записи, в C-подобный,

bool f(some_type x) { 
    if (cond) 
    return true; 
    else 
    return false; 
} 

Вместо этого, вы должны просто написать

bool f(some_type x) { 
    return cond; 
} 

Или, в этом случае

f x = cond 

Это дает короткий код

healthy :: Height -> Weight -> Bool 
healthy heightCm weightKg = let index = bmi heightCm weightKg 
          in 25 > index && 18 < index 

(Вы можете использовать пункт where тоже, но вот let только потому, что мне нравится это лучше :))

5

Во-первых, я думаю, вы, вероятно, имел в виду, чтобы определить bmi as:

bmi :: Height -> Weight -> Float 
bmi heightCm weightKg = weightKg/(heightCm/100)^2 

Поскольку формула использует высоту в метрах.

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

type PersonStats = (String, Height, Weight) 

и некоторые функции по этому типу:

healthyPerson :: PersonStats -> Bool 
healthyPerson (name, h, w) = healthy h w 

getName :: PersonStats -> String 
getName (name, h, w) = name 

С теми, на месте, конечная функция становится тривиальной:

healthyPeople :: PeopleStats -> [String] 
healthyPeople people = map getName $ filter healthyPerson people 

или в точке, свободной от обозначений :

healthyPeople :: PeopleStats -> [String] 
healthyPeople = map getName . filter healthyPerson 

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

2

Существует стандартная функция Haskell с именем filter, которая делает точно (ну, почти) то, что вы хотите здесь. Он имеет тип (a -> Bool) -> [a] -> [a], т. Е. Он берет предикат и список и возвращает члены, которые удовлетворяют предикату.

Вы не можете применить его непосредственно к PeopleStats, так как типы не совпадают, но это не так трудно написать функцию, чтобы соединить два:

healthyPerson :: (String, Height, Weight) -> Bool 
healthyPerson (_, h, w) = healthy h w 

healthyPeople :: [(String, Height, Weight)] -> [String] 
healthyPeople people = map name $ filter healthyPerson people 
    where name (s, _, _) = s 

Это делает то, что вы хотите.

+0

сделать это тоже бесплатно: 'healthyPeople = map name. фильтр здоровыйPerson' –

0
type Height = Float 
type Weight = Float 
data PersonStats = PersonStats 
    { personName :: String, personHeight :: Height, personWeight :: Weight } 

bmi :: Height -> Weight -> Float 
bmi heightCm weightKg = weightKg/(heightCm/100)^2 

healthy :: Height -> Weight -> Bool 
healthy heightCm weightKg = 25 > index && 18 < index 
    where index = bmi heightCm weightKg 

healthyPerson :: PersonStats -> Bool 
healthyPerson p = healthy (personHeight p) (personWeight p) 

healthyPeople :: [PersonStats] -> [String] 
healthyPeople = map personName . filter healthyPerson 
Смежные вопросы