Array vs List in Elm - arrays

I was suprised to learn that Array and List were two different types in Elm:
Array
List
In my case, I have a List Int of length 2,000,000 and I need about 10,000 of them but I don't know in advance which ten thousand. That will be provided by another list. In pseudo-code:
x = [ 1,1,0,30,...,255,0,1 ]
y = [ 1,4,7,18,36,..., 1334823 , ... 1899876 ]
z = [ y[x[0]], y[x[1]], ... ]
I am using pseudocode because clearly this isn't Elm syntax (it might be legal JavaScript).
Can these array selections be done in List or Array or both?

List is a linked list which provides O(n) lookup time based on index. Getting an element by index requires traversing the list over n nodes. An index lookup function for List isn't available in the core library but you can use the elm-community/list-extra package which provides two functions for lookup (varying by parameter order): !! and getAt.
Array allows for O(log n) index lookup. Index lookups on Array can be done using Array.get. Arrays are represented as Relaxed Radix Balanced Trees.
Both are immutable (all values in Elm are immutable), so you have trade-offs depending on your situation. List is great when you make a lot of changes because you are merely updating linked list pointers, whereas Array is great for speedy lookup but has somewhat poorer performance for modifications, which you'll want to consider if you're making a lot of changes.

Something like this should work:
import Array
import Debug
fromJust : Maybe a -> a
fromJust x = case x of
Just y -> y
Nothing -> Debug.crash "error: fromJust Nothing"
selectFromList : List a -> List Int -> List a
selectFromList els idxs =
let arr = Array.fromList els
in List.map (\i -> fromJust (Array.get i arr)) idxs
It converts the input list to an array for fast indexing, then maps the list of indices to their corresponding values in the array. I took the fromJust function from this StackOverflow question.

Only use Array if you need to use Array.get.
In most cases you should use List because usually you can do everything you need with foldl, map, etc. without having to get items from an index, and List has better performance with these functions.

Related

Haskell - Sum of the differences between each element in each matrix

I am very new to Haskell (and functional programming in general) and I am trying to write a function called
"profileDistance m1 m2" that takes two matrices as parameters and needs to calculate the sum of the differences between each element in each matrix... I might have not explained that very well. Let me show it instead.
The matrices are on the form of: [[(Char,Int)]]
where each matrix might look something like this:
m1 = [[('A',1),('A',2)],
[('B',3),('B',4)],
[('C',5),('C',6)]]
m2 = [[('A',7),('A',8)],
[('B',9),('B',10)],
[('C',11),('C',12)]]
(Note: I wrote the numbers in order in this example but they can be ANY numbers in any order. The chars in each row in each matrix will however match like shown in the example.)
The result (in the case above) would look something like (psuedo code):
result = ((snd m1['A'][0])-(snd m2['A'][0]))+((snd m1['A'][1])-(snd m2['A'][1]))+((snd m1['B'][0])-(snd m2['B'][0]))+((snd m1['B'][1])-(snd m2['B'][1]))+((snd m1['C'][0])-(snd m2['C'][0]))+((snd m1['C'][1])-(snd m2['C'][1]))
This would be easy to do in any language that has for-loops and is non-functional but I have no idea how to do this in Haskell. I have a feeling that functions like map, fold or sum would help me here (admittedly I am not a 100% sure on how fold works). I hope there is an easy way to do this... please help.
Here a proposal:
solution m1 m2 = sum $ zipWith diffSnd flatM1 flatM2
where
diffSnd t1 t2 = snd t1 - snd t2
flatM1 = concat m1
flatM2 = concat m2
I wrote it so that it's easier to understand the building blocks.
The basic idea is to iterate simultaneously on our two lists of pairs using zipWith. Here its type:
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
It means it takes a function with type a -> b -> c, a list of a's and a list of b's, and it returns a list of c's. In other words, zipWith takes case of the iteration, you just have to specify what you want to do with every item the iteration yields, that in your case will be a pair of pairs (one from the first matrix, another one from the second).
The function passed to zipWith takes the snd element from each pair, and computes the difference. Looking back at zipWith signature you can deduce it will return a list of numbers. So the last thing we need to do is summing them, using the function sum.
There's one last problem. We actually do not have two lists of pairs to be passed to zipWith!, but two matrices. We need to "flatten" them in a list, preserving the order of the elements. That's exactly what concat does, hence the calls to that function in the definitions of flatM1 and flatM2.
I suggest you look into the implementation of every function I mentioned to have a better grasp of how iteration is expressed by mean of recursion. HTH

How to structure multiple python arrays for sorting

A fourier analysis I'm doing outputs 5 data fields, each of which I've collected into 1-d numpy arrays: freq bin #, amplitude, wavelength, normalized amplitude, %power.
How best to structure the data so I can sort by descending amplitude?
When testing with just one data field, I was able to use a dict as follows:
fourier_tuples = zip(range(len(fourier)), fourier)
fourier_map = dict(fourier_tuples)
import operator
fourier_sorted = sorted(fourier_map.items(), key=operator.itemgetter(1))
fourier_sorted = np.argsort(-fourier)[:3]
My intent was to add the other arrays to line 1, but this doesn't work since dicts only accept 2 terms. (That's why this post doesn't solve my issue.)
Stepping back, is this a reasonable approach, or are there better ways to combine & sort separate arrays? Ultimately, I want to take the data values from the top 3 freqs and associated other data, and write them to an output data file.
Here's a snippet of my data:
fourier = np.array([1.77635684e-14, 4.49872050e+01, 1.05094837e+01, 8.24322470e+00, 2.36715913e+01])
freqs = np.array([0. , 0.00246951, 0.00493902, 0.00740854, 0.00987805])
wavelengths = np.array([inf, 404.93827165, 202.46913583, 134.97942388, 101.23456791])
amps = np.array([4.33257766e-16, 1.09724890e+00, 2.56328871e-01, 2.01054261e-01, 5.77355886e-01])
powers% = np.array([4.8508237956526163e-32, 0.31112370227749603, 0.016979224022185751, 0.010445983875848858, 0.086141014686372669])
The last 4 arrays are other fields corresponding to 'fourier'. (Actual array lengths are 42, but pared down to 5 for simplicity.)
You appear to be using numpy, so here is the numpy way of doing this. You have the right function np.argsort in your post, but you don't seem to use it correctly:
order = np.argsort(amplitudes)
This is similar to your dictionary trick only it computes the inverse shuffling compared to your procedure. Btw. why go through a dictionary and not simply a list of tuples?
The contents of order are now indices into amplitudes the first cell of order contains the position of the smallest element of amplitudes, the second cell contains the position of the next etc. Therefore
top5 = order[:-6:-1]
Provided your data are 1d numpy arrays you can use top5 to extract the elements corresponding to the top 5 ampltiudes by using advanced indexing
freq_bin[top5]
amplitudes[top5]
wavelength[top5]
If you want you can group them together in columns and apply top5 to the resulting n-by-5 array:
np.c_[freq_bin, amplitudes, wavelength, ...][top5, :]
If I understand correctly you have 5 separate lists of the same length and you are trying to sort all of them based on one of them. To do that you can either use numpy or do it with vanilla python. Here are two examples from top of my head (sorting is based on the 2nd list).
a = [11,13,10,14,15]
b = [2,4,1,0,3]
c = [22,20,23,25,24]
#numpy solution
import numpy as np
my_array = np.array([a,b,c])
my_sorted_array = my_array[:,my_array[1,:].argsort()]
#vanilla python solution
from operator import itemgetter
my_list = zip(a,b,c)
my_sorted_list = sorted(my_list,key=itemgetter(1))
You can then flip the array with my_sorted_array = np.fliplr(my_sorted_array) if you wish or if you are working with lists you can reverse it in place with my_sorted_list.reverse()
EDIT:
To get first n values only, you have to simply slice the array similarly to what #Paul is suggesting. Slice is done in a similar manner to classic list slicing by specifying start:stop:step (you can omit the step) arguments. In your case for 5 top columns it would be [:,-5:]. So in the example above you can take top 2 columns from each row like this:
my_sliced_sorted_array = my_sorted_array[:,-2:]
result will be:
array([[15, 13],
[ 3, 4],
[24, 20]])
Hope it helps.

Sort an array of arrays in Erlang

I need to sort arrays but not classical ones. Moreover, in Erlang !
I'm talking about arrays where each element is an array of two integers.
For example :
[ [6,0], [12,1], [5,2], [10,3] ]
I need this array to be sorted in function of the first element of each arrays
Like this :
[ [5,2], [6,0], [10,3], [12,1] ]
First, I succeed with this :
-module(insertSort).
-export([insertion/1,insert/1,insert/2]).
insertion(L) -> lists:foldl(fun insert/2, [], L).
insert([]) -> [].
insert(X,[]) -> [X];
insert([],Y) -> [Y];
insert(X= [X1,_], L= [[H1,_] | _]) when X1 =< H1 -> [X|L];
insert(X,[H|T]) -> [H|insert(X, T)].
Sometimes, arrays are sorted. But I have two examples that gives me two weird cases:
First :
[ [10,3], [5,2], [12,1], [6,0] ]
become :
[[[5,2], [6,0], [10,3], [12,1]]]]
Not bad but I can't work with that weird construction. It seems that I have an array, where there is an array inside, where there is all my two-elements arrays inside.
Second :
[ [5,16], [11,12], [9,8], [16,4] ]
become :
[[[5,16], "\t\b","\v\f",[16,4]]]
Yes, really, with these \t\b..
I'm a beginner in Erlang but I succeed to do a lot of things by myself.
My project is about Shank Algorithm, trying to solve y = a^x mod n where y, a and n are given.
Can you please help me by telling me what is wrong with my insertSort module ?
Thanks a lot, sorry for my english, I tried to do my best.
As I said in my comment, the sort function of the lists library does exactly what you want.
1> lists:sort( [ [6,0], [12,1], [5,2], [10,3] ]).
[[5,2],[6,0],[10,3],[12,1]]
2>
You probably shouldn't write your own sorting function. If lists:sort(List) isn't what you want for your use case, you could sort on only the first element of the sublists like this:
lists:sort(fun ([H1|_],[H2|_]) -> H1 =< H2 end,
List)
I think problem is with line:
insert([],Y) -> [Y];
Y is already a sorted list so it should be returned as it is
insert([],Y) -> Y;
Erlang strings are list of integers so when your list is not really a string but contains code points of your specified encoding, then it is printed in string notation, but it's still a list. List [65,65,67,68] is equal to "ABCD". Try to type that list into your shell, it's just an erlang pretty printing... not accurate sometimes.

Haskell iteration (not literally) over a list

I know I should be forgetting about iterating in functional languages, but I dont know how else to put forth my question.
If I have a list of integers arranged in ascending or descending order, and I have an arbitrary number that may or may not be present in the list, how can I loop over the list to find a number that is small than the given number and return that integer.
I just need to know how to go about it.
You could use find to find the first element matching a predicate you specify. Example:
find even [3,5,7,6,2,3,4]
Or, you could drop all the unwanted elements from the left:
dropWhile (not . even) [3,5,7,6,2,3,4]
(and possibly take the first element remaining, which has to be even).
Or, you could filter out unwanted elements
filter even [3,5,7,6,2,3,4]
Or, you could use recursion and code everything yourself.
You can recursively deconstruct the list with pattern matching:
searchList :: Int -> [Int] -> ???
searchList n [] = ???
searchList n (x:xs) = ???
You check whether x is the number you want, and if not you can call searchList n xs to search the rest of the list.
The normal way to do something like that would be with the library function foldr, but it would be better to understand how to do this with recursion first.
You can have "state" in a list iteration by using a fold - the state is passed from one iteration to the next in a function argument.
An example:
sup :: [Int] -> Int -> Int
sup xs y = go (head xs) xs
where
go s [] = s
go s (x:xs) = if x >= y then s else go x xs
Here s is the "state" -- it is the latest value in the list that is less than y.
Because you said the input list would never be empty, head xs is okay here.
This is almost what you want. Perhaps you can modify it to handle all of your conditions.

Growing arrays in Haskell

I have the following (imperative) algorithm that I want to implement in Haskell:
Given a sequence of pairs [(e0,s0), (e1,s1), (e2,s2),...,(en,sn)], where both "e" and "s" parts are natural numbers not necessarily different, at each time step one element of this sequence is randomly selected, let's say (ei,si), and based in the values of (ei,si), a new element is built and added to the sequence.
How can I implement this efficiently in Haskell? The need for random access would make it bad for lists, while the need for appending one element at a time would make it bad for arrays, as far as I know.
Thanks in advance.
I suggest using either Data.Set or Data.Sequence, depending on what you're needing it for. The latter in particular provides you with logarithmic index lookup (as opposed to linear for lists) and O(1) appending on either end.
"while the need for appending one element at a time would make it bad for arrays" Algorithmically, it seems like you want a dynamic array (aka vector, array list, etc.), which has amortized O(1) time to append an element. I don't know of a Haskell implementation of it off-hand, and it is not a very "functional" data structure, but it is definitely possible to implement it in Haskell in some kind of state monad.
If you know approx how much total elements you will need then you can create an array of such size which is "sparse" at first and then as need you can put elements in it.
Something like below can be used to represent this new array:
data MyArray = MyArray (Array Int Int) Int
(where the last Int represent how many elements are used in the array)
If you really need stop-and-start resizing, you could think about using the simple-rope package along with a StringLike instance for something like Vector. In particular, this might accommodate scenarios where you start out with a large array and are interested in relatively small additions.
That said, adding individual elements into the chunks of the rope may still induce a lot of copying. You will need to try out your specific case, but you should be prepared to use a mutable vector as you may not need pure intermediate results.
If you can build your array in one shot and just need the indexing behavior you describe, something like the following may suffice,
import Data.Array.IArray
test :: Array Int (Int,Int)
test = accumArray (flip const) (0,0) (0,20) [(i, f i) | i <- [0..19]]
where f 0 = (1,0)
f i = let (e,s) = test ! (i `div` 2) in (e*2,s+1)
Taking a note from ivanm, I think Sets are the way to go for this.
import Data.Set as Set
import System.Random (RandomGen, getStdGen)
startSet :: Set (Int, Int)
startSet = Set.fromList [(1,2), (3,4)] -- etc. Whatever the initial set is
-- grow the set by randomly producing "n" elements.
growSet :: (RandomGen g) => g -> Set (Int, Int) -> Int -> (Set (Int, Int), g)
growSet g s n | n <= 0 = (s, g)
| otherwise = growSet g'' s' (n-1)
where s' = Set.insert (x,y) s
((x,_), g') = randElem s g
((_,y), g'') = randElem s g'
randElem :: (RandomGen g) => Set a -> g -> (a, g)
randElem = undefined
main = do
g <- getStdGen
let (grownSet,_) = growSet g startSet 2
print $ grownSet -- or whatever you want to do with it
This assumes that randElem is an efficient, definable method for selecting a random element from a Set. (I asked this SO question regarding efficient implementations of such a method). One thing I realized upon writing up this implementation is that it may not suit your needs, since Sets cannot contain duplicate elements, and my algorithm has no way to give extra weight to pairings that appear multiple times in the list.

Resources