Skip to content

Function to convert List to Array or to sort Array by key? #942

@Boscop

Description

@Boscop

I found the function to convert a List to an Array (of), but it seems there is no function to do the reverse?
It would be useful to have it in the std library, e.g.:

let list_to_arr l = 
    match l with
        | Nil -> [] 
        | Cons head tail -> append [head] (list_to_arr tail)

Or (with tail call & accumulator):

let list_to_array l = 
    let to_array acc l =
        match l with
            | Nil -> acc
            | Cons head tail -> to_array (append acc [head]) tail
    to_array [] l

Btw, does gluon do any tail call optimization? :)

I'm asking because I'm working with a lot of long arrays and I need to often sort them.
The only sort function I found in std is for lists, but I need sort_by_key for arrays.

I tried doing

fn sort_by_key<T, K: std::cmp::Ord>(mut arr: Vec<T>, f: &dyn Fn(&T) -> K) -> Vec<T> {
	arr.sort_by_key(f);
	arr
}
// ...
ExternModule::new(vm, record! {
    sort_by_key => primitive!(2, sort_by_key),
    // ...
})

but I got all kinds of errors because of the generics and because the Fn can't cross the FFI boundary. Is there any way to make it work? :)

Then I tried to write sort_by_key for arrays in the repl, like this (based on sort for List):

let { append, Array } = import! std.array
let { List, of, ? } = import! std.list

let list_to_array l = 
    let to_array acc l =
        match l with
            | Nil -> acc
            | Cons head tail -> to_array (append acc [head]) tail
    to_array [] l

let sort_by_key arr key : forall a k. [Ord k] -> Array a -> (a -> k) -> Array a =
    let prelude @ { Ordering, ? } = import! std.prelude
    let { Semigroup, Monoid, Eq, Show } = prelude
    let { Functor, Applicative, Alternative, Monad } = prelude
    let { Foldable } = import! std.foldable
    let { Traversable } = import! std.traversable
    let { Bool } = import! std.bool
    let array @ { ? } = import! std.array
    let { (<>) } = import! std.semigroup
    let { compare } = import! std.cmp
    let { map } = import! std.functor
    let { (<*>), wrap } = import! std.applicative
    let { (<|>) } = import! std.alternative
    let { List, of, ? } = import! std.list
    rec let scan compare xs less equal greater : (a -> Ordering)
            -> List a
            -> List a
            -> List a
            -> List a
            -> (List a, List a, List a)
        =
        match xs with
        | Nil -> (less, equal, greater)
        | Cons y ys ->
            match compare y with
            | LT -> scan compare ys (Cons y less) equal greater
            | EQ -> scan compare ys less (Cons y equal) greater
            | GT -> scan compare ys less equal (Cons y greater)
    in
    rec
    let sort xs : /*[Ord a] ->*/ List a -> List a =
        match xs with
        | Nil -> Nil
        | Cons pivot ys ->
            let (less, equal, greater) = scan (\a -> compare (key a) (key pivot)) ys Nil (Cons pivot Nil) Nil
            sort less <> equal <> sort greater
    list_to_array (sort (of arr))

sort_by_key [{ t = 1., k = "a" }, {t = 0., k = "b" }] (\e -> e.t)

It works but it's quite slow.
Ideally I'd want to use the FFI because I'm working with large arrays of thousands of items and the sorting should be fast if possible :)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions