Zip(list_of_lists)

so i look up the zip(list_of_lists) in github and it have some example

Zips corresponding elements from each list in list_of_lists.
The zipping finishes as soon as any list terminates.

Examples

      iex> List.zip([[1, 2], [3, 4], [5, 6]])
      [{1, 3, 5}, {2, 4, 6}]
      iex> List.zip([[1, 2], [3], [5, 6]])
      [{1, 3, 5}]

i want to make the def function like above

def zip([]), do: []

def zip(list_of_lists) when is_list(list_of_lists) do
    do_zip(list_of_lists, [])
end

i want to understand what is list of lists and how to represent it when every list is not the same, thanks

If I understand, you want to reimplement List.zip\1, is that correct? That’s a nice exercise.

The list_of_lists argument is, as the name says, a list of several lists, possibly of different lengths. The zip function iterates through all the lists, grouping the nth element from each list into a tuple. It stops as soon as the shortest list is exhausted.

What exactly you want to understand better? Is something in particular confusing you?

i am trying to put the code into the exs and run it with erlang

	def cross([]) do
	0
	end

	def cross(list_of_lists) when is_list(list_of_lists) do
    cross(list_of_lists, [])
	end

and the return is said is a undefined function, so i want know is that anything just wrong since i cannot compilation with these code

Well, there are a number of things that are wrong. First of all, the function must be defined within a module. Second, the recursive call to cross uses the wrong number of arguments (cross is defined with one argument, but is called with two arguments in cross(list_of_lists, [])).

Also, this recursive call would never complete, because the list_of_list is passed unchanged, and therefore will never get to the base case of empty list (unless it’s empty to start with).

Usually, when implementing recursion, you start with a base case, and you make sure that the recursive call moves in the direction of the base case (if the base case is an empty list, the list should be consumed at each recursive call, so that it will eventually be empty).

For example, let’s define a function that takes a list of numbers and returns the sum:

defmodule Sum do
  # Version of sum with one single argument, calling the two
  # arguments version with the initial `partial_sum` of 0
  def sum(list) do
    sum(list, 0)
  end

  # Base case, the given list is empty, so we can just return the
  # sum accumulated so far
  def sum([], partial_sum) do
    partial_sum
  end

  # If the list is not empty, we sum the first element and call
  # the function recursively with the rest of the list, so it shrinks
  # at each call, eventually becoming empty
  def sum([first | rest], partial_sum) do
    sum(rest, partial_sum + first)
  end
end

Now, you can call the defined function:

Sum.sum([1, 2, 3, 4, 5])
#=> 15
defmodule Ans0 do
	def cross([],[]) do
	0
	end
	
	def cross([h1|t1],[h2|t2]) do
	{h1,h2}
	cross([t1],[t2])
	end

end

so it should be like this?

No.

The {h1, h2} line does not have any effect, its an unused value.

The cross([t1], [t2]) line will eventually just loop forever as cross([[]], [[]]). You do not need to wrap the tails in a list, as they already are lists.

Also your base case will just return 0, which would make your function return 0 regardless of the input, if there wasn’t the infinite loop in the other case. As you want to construct a list in the function, you need to return a list. What do you think how should the return look like?

In the other case, you need to construct the return value from the tuple you already build and forget, and the result of the recursive call. Something like [the_tuple | result_of_the_recursive_call].

Well, you are making progresses, but there are still things that won’t really work in this code.

First of all, the first definition of cross, for empty lists, returns 0. I don’t know what you are trying to do (are you implementing zip?), but that seems at odd with the rest of the program. What is your expected output? If you are using recursion, your base clause should return that.

Second, the tuple {h1, h2} is created but never used nor returned, so right now it is useless. If you are implementing zip, you’ll want to accumulate these tuples to finally return them in a list.

Remember that Elixir functions always return the result of their last expression.

Also, t1 and t2 are already lists. If you wrap them in brackets you will get nested lists, which is probably not what you want.

Here’s a nudge in the right direction:

defmodule Ans0 do
  # In the public 2 argument version you can call a private
  # 3 arguments version with an added argument to accumulate
  # step by step the final result
  def cross(a, b) do
    cross(a, b, [])
  end

  defp cross([], [], accumulated) do
    # I leave this to you, reason on what must be returned
    # when you reach the base case
  end

  defp cross([head1 | tail1], [head2 | tail2], accumulated) do
    # Here we prepend {head1, head2} to the accumulated
    # result, and pass it with the tails of the lists to the next
    # iteration
    cross(tail1, tail2, [{head1, head2} | accumulated])
  end
end

When implementing it, pay attention to the order of the accumulated list, and also beware that this implementation would only work if the list arguments have the same length. You can add base cases to account for different lengths (I leave this to you).

In general, my recommendation is that you take your time to learn the basics of Elixir before you jump into recursion exercises: it will make it easier to understand why something is not working as it should.

question

this is what i try to do about it

1 Like

update:

defmodule Ans0 do

	def cross([],[]) do
	end

	def cross([h1|t1],[h2|t2]) do
	[{h1,h2}|cross(t1,t2)]
	end

end

This returns nil, so your final result will be [{'a', 1},{'b', 2}, {'c', 3} | nil] which is called an improper list because it contains a non-list element as the final element (well it is more complicated but I can’t explain it properly).
Note that
[{'a', 1}, {'b', 2}, {'c', 3} | nil]
and
[{'a', 1}, {'b', 2}, {'c', 3}, nil]

are different.

The first one is equivalent to
[{'a', 1} | [{'b', 2} | [{'c', 3} | nil]]]
The second one to
[{'a', 1} | [{'b', 2} | [{'c', 3} | [nil | []]]]]

2 Likes

You are very close. As @lud explained, you still have an issue with the base case.

In order to solve it for good, think about your base case: if you would call Ans0.cross([], []), what would you expect as a result? Right now, you get nil, but is this what you really should expect? Remember that in Elixir nil is different from [].