Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to manipulate list elements in F#

I'm currently working my way through a project using F#. I'm quite new to functional programming, and while I'm familiar with the idea of list items being immutable, I'm still having a bit of an issue:

I have a list of strings of the format

["(states, (1,2,3,4,5))"; "(alpha, (1,2))"; "(final, (1))"]

What I would like to do is turn each list element into its own list without the initial comma separated string. The output should look something like this:

["1"; "2"; "3"; "4"; "5"]
["1"; "2"]
["1"]

I've found myriad ways to concatenate list elements and my best guesses thus far (unfolding, or something of the sort) have been fruitless. Any help or a point in the right direction would be much appreciated. Thanks!

like image 240
is0 Avatar asked Feb 22 '16 17:02

is0


People also ask

How to add an element to a list in f#?

Operators for Working with Lists You can attach elements to a list by using the :: (cons) operator. If list1 is [2; 3; 4] , the following code creates list2 as [100; 2; 3; 4] . You can concatenate lists that have compatible types by using the @ operator, as in the following code.

How do you define a list in F#?

F# Lists Creating lists A way to create a list is to place elements in two square brackets, separated by semicolons. The elements must have the same type. It is also possible to define lists of functions, of elements of a type defined previously, of objects of a class, etc.

What is the value assigned as index to the first element in a list?

In any list the first element is assigned index value 0 and the last element can be considered as a value -1.


2 Answers

Just for the fun of it, here's an outline of how to parse the strings using FParsec, a parser combinator library.

First, you import some modules:

open FParsec.Primitives
open FParsec.CharParsers

Then, you can define a parser that will match all strings enclosed by parentheses:

let betweenParentheses p s = between (pstring "(") (pstring ")") p s

This will match any string enclosed in parentheses, such as "(42)", "(foo)", "(1,2,3,4,5)", etc., depending on the specific parser p passed as the first argument.

In order to parse numbers like "(1,2,3,4,5)" or "(1,2)", you can combine betweenParentheses with FParsec's built-in sepBy and pint32:

let pnumbers s = betweenParentheses (sepBy pint32 (pstring ",")) s

pint32 is a parser of integers, and sepBy is a parser that reads a list of values, separated by a string - in this case ",".

In order to parse an entire 'group' of values, such as "(states, (1,2,3,4,5))" or "(alpha, (1,2))", you can again use betweenParentheses and pnumbers:

let pgroup s =
    betweenParentheses
        (manyTill anyChar (pstring ",") >>. spaces >>. pnumbers) s

The manyTill combination parses any char value until it encounters ,. Next, the pgroup parser expects any number of spaces, and then the format defined by pnumbers.

Finally, you can define a function that runs the pgroup parser on a string:

// string -> int32 list option
let parseGroup s =
    match run pgroup s with
    | Success (result, _, _) -> Some result
    | Failure _              -> None

Since this function returns an option, you can use List.choose to map the strings that can be parsed:

> ["(states, (1,2,3,4,5))"; "(alpha, (1,2))"; "(final, (1))"]
  |> List.choose parseGroup;;
val it : int32 list list = [[1; 2; 3; 4; 5]; [1; 2]; [1]]

Using FParsec is most likely overkill, unless you have some more flexible formatting rules than what can easily be addressed with .NET's standard string API.

like image 105
Mark Seemann Avatar answered Sep 20 '22 13:09

Mark Seemann


You can also just use Char.IsDigit (at least based on your sample data) like so:

open System

// Signature is string -> string list
let getDigits (input : string) =
    input.ToCharArray()
    |> Array.filter Char.IsDigit
    |> Array.map (fun c -> c.ToString())
    |> List.ofArray

// signature is string list -> string list list
let convertToDigits input =
    input
    |> List.map getDigits

And testing it out in F# interactive:

> let sampleData = ["(states, (1,2,3,4,5))"; "(alpha, (1,2))"; "(final, (1))"];;

val sampleData : string list =
  ["(states, (1,2,3,4,5))"; "(alpha, (1,2))"; "(final, (1))"]

> let test = convertToDigits sampleData;;

val test : string list list = [["1"; "2"; "3"; "4"; "5"]; ["1"; "2"]; ["1"]]

NOTE: If you have more than 1 digit numbers, this will split them into individual elements in the list. If you don't want that you'll have to use regex or string.split or something else.

like image 28
Jason Down Avatar answered Sep 21 '22 13:09

Jason Down