I have a string that can be like this: "100.4", "100.4867", "100.0". I'd like to truncate it so that it has at most two digits after the period. So for example: "100.4", "100.48", "100.0". What's an efficient way to do this in Erlang?
If you sure, that strings contains right representation of float , can do, for example, so:
List = [ "100.4", "100.4867", "100.0"].
[fun()->lists:sublist(X,string:chr(X,$.)+2) end() || X<-List].
and result:
["100.4","100.48","100.0"]
if no - add the processing of these cases.
As rightly noted Lyn Headley in the comments anonymous function here is not required, and you can do so:
[lists:sublist(X,string:chr(X,$.)+2) || X<-List].
If you ask about efficient implementation:
trunc([]) -> []; %% or raise exception because it is not a float
trunc(".") -> []; %% or "." = L) -> L or raise exception or ".0" or what ever you want
trunc([$.,_] = L) -> L;
trunc([$.,_,_] = L) -> L;
trunc([$.,X,Y|_]) -> [$.,X,Y];
trunc([H|T]) -> [H|trunc(T)].
Related
-module(solarSystem).
-export([process_csv/1, is_numeric/1, parseALine/2, parse/1, expandT/1, expandT/2,
parseNames/1]).
parseALine(false, T) ->
T;
parseALine(true, T) ->
T.
parse([Name, Colour, Distance, Angle, AngleVelocity, Radius, "1" | T]) ->
T;%Where T is a list of names of other objects in the solar system
parse([Name, Colour, Distance, Angle, AngleVelocity, Radius | T]) ->
T.
parseNames([H | T]) ->
H.
expandT(T) ->
T.
expandT([], Sep) ->
[];
expandT([H | T], Sep) ->
T.
% https://rosettacode.org/wiki/Determine_if_a_string_is_numeric#Erlang
is_numeric(L) ->
S = trim(L, ""),
Float = (catch erlang:list_to_float(S)),
Int = (catch erlang:list_to_integer(S)),
is_number(Float) orelse is_number(Int).
trim(A) ->
A.
trim([], A) ->
A;
trim([32 | T], A) ->
trim(T, A);
trim([H | T], A) ->
trim(T, A ++ [H]).
process_csv(L) ->
X = parse(L),
expandT(X).
The problem is that it will calls process_csv/1 function in my module in a main, L will be a file like this:
[["name "," col"," dist"," a"," angv"," r "," ..."],["apollo11 ","white"," 0.1"," 0"," 77760"," 0.15"]]
Or like this:
["planets ","earth","venus "]
Or like this:
["a","b"]
I need to display it as follows:
apollo11 =["white", 0.1, 0, 77760, 0.15,[]];
Planets =[earth,venus]
a,b
[[59],[97],[44],[98]]
My problem is that no matter how I make changes, it can only show a part, and there are no symbols. The list cannot be divided, so I can't find a way.
In addition, because Erlang is a niche programming language, I can't even find examples online.
So, can anyone help me? Thank you, very much.
In addition, I am restricted from using recursion.
I think the first problem is that it is hard to link what you are trying to achieve with what your code says thus far. Therefore, this feedback maybe is not exactly what you are looking for, but might give some ideas. Let's structure the problem into the common elements: (1) input, (2) process, and (3) output.
Input
You mentioned that L will be a file, but I assume it is a line in a file, where each line can be one of the 3 (three) samples. In this regard, the samples also do not have consistent pattern.For this, we can build a function to convert each line of the file into Erlang term and pass the result to the next step.
Process
The question also do not mention the specific logic in parsing/processing the input. You also seem to care about the data type so we will convert and display the result accordingly. Erlang as a functional language will naturally be handling list, so on most cases we will need to use functions on lists module
Output
You didn't specifically mention where you want to display the result (an output file, screen/erlang shell, etc), so let's assume you just want to display it in the standard output/erlang shell.
Sample file content test1.txt (please note the dot at the end of each line)
[["name "," col"," dist"," a"," angv"," r "],["apollo11 ","white","0.1"," 0"," 77760"," 0.15"]].
["planets ","earth","venus "].
["a","b"].
Howto run: solarSystem:process_file("/Users/macbook/Documents/test1.txt").
Sample Result:
(dev01#Macbooks-MacBook-Pro-3)3> solarSystem:process_file("/Users/macbook/Documents/test1.txt").
apollo11 = ["white",0.1,0,77760,0.15]
planets = ["earth","venus"]
a = ["b"]
Done processing 3 line(s)
ok
Module code:
-module(solarSystem).
-export([process_file/1]).
-export([process_line/2]).
-export([format_item/1]).
%%This is the main function, input is file full path
%%Howto call: solarSystem:process_file("file_full_path").
process_file(Filename) ->
%%Use file:consult to convert the file content into erlang terms
%%File content is a dot (".") separated line
{StatusOpen, Result} = file:consult(Filename),
case StatusOpen of
ok ->
%%Result is a list and therefore each element must be handled using lists function
Ctr = lists:foldl(fun process_line/2, 0, Result),
io:format("Done processing ~p line(s) ~n", [Ctr]);
_ -> %%This is for the case where file not available
io:format("Error converting file ~p due to '~p' ~n", [Filename, Result])
end.
process_line(Term, CtrIn) ->
%%Assume there are few possibilities of element. There are so many ways to process the data as long as the input pattern is clear.
%%We basically need to identify all possibilities and handle them accordingly.
%%Of course there are smarter (dynamic) ways to handle them, but below may give you some ideas.
case Term of
%%1. This is to handle this pattern -> [["name "," col"," dist"," a"," angv"," r "],["apollo11 ","white"," 0.1"," 0"," 77760"," 0.15"]]
[[_, _, _, _, _, _], [Name | OtherParams]] ->
%%At this point, Name = "apollo11", OtherParamsList = ["white"," 0.1"," 0"," 77760"," 0.15"]
OtherParamsFmt = lists:map(fun format_item/1, OtherParams),
%%Display the result to standard output
io:format("~s = ~p ~n", [string:trim(Name), OtherParamsFmt]);
%%2. This is to handle this pattern -> ["planets ","earth","venus "]
[Name | OtherParams] ->
%%At this point, Name = "planets ", OtherParamsList = ["earth","venus "]
OtherParamsFmt = lists:map(fun format_item/1, OtherParams),
%%Display the result to standard output
io:format("~s = ~p ~n", [string:trim(Name), OtherParamsFmt]);
%%3. Other cases
_ ->
%%Display the warning to standard output
io:format("Unknown pattern ~p ~n", [Term])
end,
CtrIn + 1.
%%This is to format the string accordingly
format_item(Str) ->
StrTrim = string:trim(Str), %%first, trim it
format_as_needed(StrTrim).
format_as_needed(Str) ->
Float = (catch erlang:list_to_float(Str)),
case Float of
{'EXIT', _} -> %%It is not a float -> check if it is an integer
Int = (catch erlang:list_to_integer(Str)),
case Int of
{'EXIT', _} -> %%It is not an integer -> return as is (string)
Str;
_ -> %%It is an int
Int
end;
_ -> %%It is a float
Float
end.
I'm trying to go from:
sprintf "%3.1f" myNumber
to:
sprintf myFormatter myNumber
which is not possible
I have a situation where number precision depends on some settings, so I would like to be able to create my own formatter string.
I know it can be done with String.Format, but I am curious if there is a F# way with sprintf, or ksprinf; can it be done?
Simple answer
EDIT: Diego Esmerio on F# Slack showed me a simpler way that I honestly never thought of while working out the answer below. The trick is to use PrintfFormat directly, like as follows.
// Credit: Diego. This
let formatPrec precision =
PrintfFormat<float -> string,unit,string,string>(sprintf "%%1.%if" precision)
let x = 15.234
let a = sprintf (formatPrec 0) x
let b = sprintf (formatPrec 1) x
let c = sprintf (formatPrec 3) x
Output:
val formatPrec : precision:int -> PrintfFormat<(float -> string),unit,string,string>
val x : float = 15.234
val a : string = "15"
val b : string = "15.2"
val c : string = "15.234"
This approach is arguably much simpler than the Expr-based approach below. For both approaches, be careful with the formatting string, as it will compile just fine, but break at runtime if it is invalid.
Original answer (complex)
This isn't trivial to do, because functions like sprintf and printfn are compile-time special-case functions that turn your string-argument into a function (in this case of type float -> string).
There are some things you can do with kprintf, but it won't allow the formatting-argument to become a dynamic value, since the compiler still wants to type-check that.
However, using quotations we can build such function ourselves. The easy way is to create quotation from your expression and to change the parts we need to change.
The starting point is this:
> <# sprintf "%3.1f" #>
val it : Expr<(float -> string)> =
Let (clo1,
Call (None, PrintFormatToString,
[Coerce (NewObject (PrintfFormat`5, Value ("%3.1f")), PrintfFormat`4)]),
Lambda (arg10, Application (clo1, arg10)))
...
That may look like a whole lot of mess, but since we only need to change one tiny bit, we can do this rather simply:
open Microsoft.FSharp.Quotations // part of F#
open Microsoft.FSharp.Quotations.Patterns // part of F#
open FSharp.Quotations.Evaluator // NuGet package (with same name)
// this is the function that in turn will create a function dynamically
let withFormat format =
let expr =
match <# sprintf "%3.1f" #> with
| Let(var, expr1, expr2) ->
match expr1 with
| Call(None, methodInfo, [Coerce(NewObject(ctor, [Value _]), mprintFormat)]) ->
Expr.Let(var, Expr.Call(methodInfo, [Expr.Coerce(Expr.NewObject(ctor, [Expr.Value format]), mprintFormat)]), expr2)
| _ -> failwith "oops" // won't happen
| _ -> failwith "oops" // won't happen
expr.CompileUntyped() :?> (float -> string)
To use this, we can now simply do this:
> withFormat "%1.2f" 123.4567899112233445566;;
val it : string = "123.46"
> withFormat "%1.5f" 123.4567899112233445566;;
val it : string = "123.45679"
> withFormat "%1.12f" 123.4567899112233445566;;
val it : string = "123.456789911223"
Or like this:
> let format = "%0.4ef";;
val format : string = "%0.4ef"
> withFormat format 123.4567899112233445566;;
val it : string = "1.2346e+002f"
It doesn't matter whether the format string is now a fixed string during compile time. However, if this is used in performance sensitive area, you may want to cache the resulting functions, as recompiling an expression tree is moderately expensive.
Is there any particular reason why piping to int works here but the system convert doesn't? Which method should be used?
printfn "%i" ("1_2" |> int)
printfn "%i" (System.Int32.Parse("1_2"))
I am using .NET core 2.2
It looks like the first method calls
FSharp.Core.dll!Microsoft.FSharp.Core.LanguagePrimitives.ParseInt32(string s)
and the second calls
System.Private.CoreLib.dll!int.Parse(string s)
So if anyone is curious, I looked at the difference. the .NET core dll doesnt strip underscores
System.Private.CoreLib.dll uses
private static unsafe void StringToNumber(ReadOnlySpan<char> str, NumberStyles options, ref NumberBuffer number, NumberFormatInfo info, bool parseDecimal)
{
Debug.Assert(info != null);
fixed (char* stringPointer = &MemoryMarshal.GetReference(str))
{
char* p = stringPointer;
if (!ParseNumber(ref p, p + str.Length, options, ref number, info, parseDecimal)
|| (p - stringPointer < str.Length && !TrailingZeros(str, (int)(p - stringPointer))))
{
throw new FormatException(SR.Format_InvalidString);
}
}
}
And FSharp.Core.dll uses
let ParseInt32 (s:string) =
if System.Object.ReferenceEquals(s,null) then
raise( new System.ArgumentNullException("s") )
let s = removeUnderscores (s.Trim())
let l = s.Length
let mutable p = 0
let sign = getSign32 s &p l
let specifier = get0OXB s &p l
if p >= l then formatError() else
match Char.ToLowerInvariant(specifier) with
| 'x' -> sign * (int32OfUInt32 (Convert.ToUInt32(UInt64.Parse(s.Substring(p), NumberStyles.AllowHexSpecifier,CultureInfo.InvariantCulture))))
| 'b' -> sign * (int32OfUInt32 (Convert.ToUInt32(parseBinaryUInt64 (s.Substring(p)))))
| 'o' -> sign * (int32OfUInt32 (Convert.ToUInt32(parseOctalUInt64 (s.Substring(p)))))
| _ -> Int32.Parse(s, NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture)
As others said two different implementations of parsing an integer is used and doesn't necessarily produce the same result. However, one might wonder why F# allows 1_2 as a valid int?
I browsed the history of the source code and found that it was implemented in this commit: implement Underscore Literals
It was made to support literals in F# like this:
let x = 1_000_000
let y = 1000000
x = y // true
x and y is equal but 1_000_000 is thanks to the underscores somewhat easier to read as 1 million.
Because how it was implemented it also leaked into runtime as now int "1_000_000" parse successfully.
In the first case ("1_2" |> int) you are using int which is an F# primitive.
In the second case (System.Int32.Parse("1_2")) you are using System.Int32 which is a .NET CLR and not specifically an F# type.
The two use different rules for parsing integers as you discovered in the implementations.
type unit_t = (string * int) list
How would I go about creating an expression to parse a string into this form? Do I need to implement code in a parser.mly format in order to handle this?
There's no real way to give a detailed answer, since you don't say how the original string is supposed to correspond to your list of pairs.
Based just on the simplicity of your output type, I'd say you could do this without invoking the full machinery of ocamlyacc. I.e., I doubt you need a parser.mly file. You could use ocamllex, or you could just apply some regular expressions from the Str module. It depends on what your parsing actually needs to do.
Here's one possibility that uses regular expressions from Str:
let p s =
let fs = Str.full_split (Str.regexp "[0-9]+") s in
let rec go = function
| [] -> []
| Str.Text s :: Str.Delim i :: rest -> (s, int_of_string i) :: go rest
| Str.Text s :: rest -> (s, 0) :: go rest
| Str.Delim i :: rest -> ("", int_of_string i) :: go rest
in
go fs
You can run it like this:
# #load "str.cma";;
# #use "p.ml";;
val p : string -> (string * int) list = <fun>
# p "123abc456def";;
- : (string * int) list = [("", 123); ("abc", 456); ("def", 0)]
# p "ghi789jkl100";;
- : (string * int) list = [("ghi", 789); ("jkl", 100)]
I am having Data like the below:
Data = [{<<"status">>,<<"success">>},
{<<"META">>,
{struct,[{<<"createdat">>,1406895903.0},
{<<"user_email">>,<<"gopikrishnajonnada#gmail.com">>},
{<<"campaign">>,<<"5IVUPHE42HP1NEYvKb7qSvpX2Cm">>}]}},
{<<"mode">>,1}]
And Now i am having a
FieldList = ['<<"5IVUPHE42HP1NEYvKb7qSvpX2Cm">>']
Now:
I am trying like the below but i am getting empty instead of the value
90> [L || L <- FieldList,proplists:get_value(<<"campaign">>,element(2,proplists:get_value(<<"META">>,Data,{[],[]}))) == L].
[]
so how to get the both values are equal and get the final value.
You can parse the atom as if it were an Erlang term:
atom_to_binary(Atom) ->
L = atom_to_list(Atom),
{ok, Tokens, _} = erl_scan:string(L ++ "."),
{ok, Result} = erl_parse:parse_term(Tokens),
Result.
You can then do
[L ||
L <- FieldList,
proplists:get_value(<<"campaign">>,
element(2,
proplists:get_value(<<"META">>,Data,{[],[]})))
== atom_to_binary(L)
].
You can also do it the other way round, (trying to) convert the binary to an atom using this function:
binary_literal_to_atom(Binary) ->
Literal = lists:flatten(io_lib:format("~p", [Binary])),
try
list_to_existing_atom(Literal)
catch
error:badarg -> undefined
end.
This function will return undefined if the atom is not known yet (s. Erlang: binary_to_atom filling up atom table space security issue for more information on this). This is fine here, since the match can only work if the atom was known before, in this case by being defined in the FieldList variable.
How did you get those values in the first place?
Data = [{<<"status">>,<<"success">>},
{<<"META">>,
{struct,[{<<"createdat">>,1406895903.0},
{<<"user_email">>,<<"gopikrishnajonnada#gmail.com">>},
{<<"campaign">>,<<"5IVUPHE42HP1NEYvKb7qSvpX2Cm">>}]
}
},
{<<"mode">>,1}].
[_,{_,{struct,InData}}|_] = Data.
[X || {<<"campaign">>,X} <- InData].
it gives you the result in the form : [<<"5IVUPHE42HP1NEYvKb7qSvpX2Cm">>]
of course you can use the same kind of code if the tuple {struct,InData} may be in a different place in the Data variable.
-module(wy).
-compile(export_all).
main() ->
Data = [{<<"status">>,<<"success">>},
{<<"META">>,
{struct,[{<<"createdat">>,1406895903.0},
{<<"user_email">>,<<"gopikrishnajonnada#gmail.com">>},
{<<"campaign">>,<<"5IVUPHE42HP1NEYvKb7qSvpX2Cm">>}]
}
},
{<<"mode">>,1}],
Fun = fun({<<"META">>, {struct, InData}}, Acc) ->
Value = proplists:get_value(<<"campaign">>, InData, []),
[Value | Acc];
(_Other, Acc)->
Acc
end,
lists:foldl(Fun, [], Data).
I think you can use this code.