简体   繁体   中英

Arity issue in Elixir when passing a list to a function

I'm new to Elixir (and functional). I'm building a toy poker game to get more familiar with the language. When I compile this code, I get the following error:

** (BadArityError) #Function<12.99386804/2 in :erl_eval.expr/5> with arity 2 
called with 1 argument ('SK')

Which seems to happen whenever I pass a list as an argument. The error is occurring inside Hand.burn_cards .

import Enum

defmodule Deck do
  def create do
    for rank <- '23456789TJQKA', suit <- 'CDHS', do: [suit,rank] |> shuffle
  end

  def deal(deck, n) do
    deck 
    |> shuffle 
    |> take(n)
  end
end

defmodule Hand do
  def burn_cards(current_hand, cards = []) do
    Enum.filter(current_hand, fn (x, cards) -> x not in cards end)
  end
end

hand = Deck.deal(deck = Deck.create, 5)
deck = deck -- hand
Hand.burn_cards(hand, [Enum.at(hand, 0)])

Is this part of the language? Or am I making a mistake?

I can see two issues with your code:

first, this line

 def burn_cards(current_hand, cards = []) do

implies that the second argument passed in must be empty list but you are not passing in an empty list, you are passing a list with one element. I believe you want to use this instead

  def burn_cards(current_hand, cards \\ []) do

which means that default value is empty list if omitted, but also accepts lists with elements.

And secondly the filtering part

 Enum.filter(current_hand, fn (x, cards) -> x not in cards end)

you should not have cards in there. Instead you should try

 Enum.filter(current_hand, fn (x) -> x not in cards end)

I believe your problem is the second parameter on the anonymous function passed to Enum.filter/2 . It seems this function must receive only one argument. This is, we could do this:

iex(3)> pairs = [2, 4]                               
[2, 4]
iex(4)> Enum.filter([1, 2, 3, 4, 5], fn (x) -> x in pairs  end)    
[2, 4]

But not this:

iex(5)> pairs
[2, 4]
iex(6)> Enum.filter([1, 2, 3, 4, 5], fn (x, pairs) -> x in pairs  end)      
** (BadArityError) #Function<12.118419387/2 in :erl_eval.expr/5> with arity 2 called with 1 argument (1)
    (elixir) lib/enum.ex:2857: Enum.filter_list/2

The solution to this problem is to use the first form. So your Enum.filter call could look like this:

Enum.filter(current_hand, fn (x) -> x not in cards end)

Also, I had another problem:

** (FunctionClauseError) no function clause matching in Hand.burn_cards/2    

The following arguments were given to Hand.burn_cards/2:

    # 1
    ['7S', 'S5', 'S4', 'D7', 'H7']

    # 2
    ['7S']

help.exs:16: Hand.burn_cards/2
(elixir) lib/code.ex:376: Code.require_file/2

This was happening because the match cards = [] only succeeds when cards is also an empty list. So your function call

Hand.burn_cards(hand, [Enum.at(hand, 0)])

will never match. You may want to define burn_cards/2 like

def burn_cards(current_hand, cards) do
  Enum.filter(current_hand, fn (x) -> x not in cards end)
end

Or, if you really want to ensure cards is a list, you can do

def burn_cards(current_hand, cards) when is_list(cards) do
  Enum.filter(current_hand, fn (x) -> x not in cards end)
end

Hope I could help. =)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM