I would like to know how check if a string is a composition of another in the case bellow :
isIn("xy","xyxy") -> true
isIn("xy","xyxyx") -> true
isIn("xy","xyxyy") -> false
I have done this :
isIn(X,Y) ->
case string:substr(Y,1,length(X)) == X of
true -> true;
false -> false
end
but I dont know how to continue checking the rest of the string .
Thank you
One approach is to "rotate" the substring while working through the string you're checking, comparing character by character:
is_in(_, []) -> true;
is_in([H|T1], [H|T2]) -> is_in(T1++[H], T2);
is_in(_,_) -> false.
The first clause of is_in/2 states that if we've checked the entire second argument such that it's now the empty string, then the answer is true. The second clause verifies via pattern matching that the heads of the two strings are identical, and if so, calls recursively with the head of the first string rotated to its tail and the head of the second string dropped. The final clause of is_in/2 returns false to handle the case of the heads of the strings not matching.
There are likely additional checks you need on the initial arguments, such as ensuring the length of the first string is less than or equal to that of the second string, ensuring that neither string is empty, etc.
There is better performant solution
is_in(S1, S2) ->
is_in(S1, false, S1, S2).
is_in(S1, A, [H|T1], [H|T2]) ->
is_in(S1, A, T1, T2);
is_in([_|_] = S1, _, [], S2) ->
is_in(S1, true, S1, S2);
is_in(_, A, _, []) -> A;
is_in(_, _, _, _) -> false.
Related
I'm wondering how can i define function, which as an argument takes a string and returns a member of the record.
For example with the record
-record(measurement, { temperature, pm2p5, pm10, pressure, humidity, others=[]}).
And the fragment of my function:
update_measurement(Measurement, Type_as_String, Value) ->
Measurement#measurement{get_type(Type_as_String) = Value}
I want to update a value by passing a type as string, and I don't have an idea to define the function get_type(Type_as_String).
I've tried with atoms, but it didn't work.
Something like
update_measurement(Measurement, Type_as_String, Value) ->
case Type_as_String of
"temperature" -> Measurement#measurement{temperature = Value};
"humidity" -> Measurement#measurement{humidity = Value};
...
isn't ok, because i want to reuse this pattern in other functions.
If performance is not your biggest concern:
update_measurement(Measurement, Type_as_String, Value) ->
update_field(Measurement, Type_as_String, Value).
update_field(#measurement{} = Record, SKey, Value) ->
update_field(Record, SKey, Value, record_info(fields, measurement));
% add other records here
update_field(_, _, _) -> error(bad_record).
update_field(Record, SKey, Value, Fields) ->
update_field(Record, list_to_existing_atom(SKey), Value, Fields, 2).
update_field(Record, Key, Value, [Key|_], N) ->
setelement(N, Record, Value);
update_field(Record, Key, Value, [_|Fields], N) ->
update_field(Record, Key, Value, Fields, N+1);
update_field(_, _, _, [], _) ->
error(bad_key).
Note record_info/2 is not a real function but you have to provide measurement as a compile time constant.
I'm currently writing a web-based vocabulary trainer in Elm. This requires sorting a list of words by a custom comparator.
The type I want to sort is:
type alias Word =
{ id: Int
, sourceWord: String
, targetWord: String
, numTries: Int
, numCorrect: Int
, createdAt: Maybe Date -- might be empty, therefore wrapped in Maybe
, lastAskedAt: Maybe Date -- might be empty, therefore wrapped in Maybe
}
type alias WordList = List (Word)
My rules for comparison are (in descending order of importance):
number of correct guesses (asc)
number overall guesses (desc)
when word was last asked (asc)
when word was added (desc)
The best approach I could come up with is this:
compareWords: Word -> Word -> Basics.Order
compareWords w1 w2 =
let
dateToComparable d = Date.Format.format "%Y-%m-%d" d
orderNumCorrect = compare w1.numCorrect w2.numCorrect
orderNumTries = compare w2.numTries w1.numTries -- switch ordering to sort descending
orderLastAskedAt = case (w1.lastAskedAt, w2.lastAskedAt) of
(Just a1, Just a2) -> compare (dateToComparable a1) (dateToComparable a2)
(Nothing, Just _) -> Basics.LT
(Just _, Nothing) -> Basics.GT
(Nothing, Nothing) -> Basics.EQ
orderCreatedAt = case (w2.createdAt, w1.createdAt) of -- switch ordering to sort descending
(Just a1, Just a2) -> compare (dateToComparable a1) (dateToComparable a2)
(Nothing, Just _) -> Basics.LT
(Just _, Nothing) -> Basics.GT
(Nothing, Nothing) -> Basics.EQ
in
case orderNumCorrect of
Basics.EQ -> case orderNumTries of
Basics.EQ -> case orderLastAskedAt of
Basics.EQ -> orderCreatedAt
_ -> orderLastAskedAt
_ -> orderNumTries
_ -> orderNumCorrect
which I don't like for a number of reasons:
it's ugly as hell
it requires me to use Date.Format.format (from mgold/elm-date-format) to compare Date values (since Date apparently is not comparable)
Is there a more elegant / Elm-ish way to achieve what I want?
Update + solution
As #"Zimm i48" suggested in their most excellent answer, here's a much shorter version that uses the elm-ordering package:
dateToComparable : Maybe Date -> Time
dateToComparable =
Maybe.map Date.toTime >> Maybe.withDefault 0
compareWords : Ordering Word
compareWords =
Ordering.byField .numCorrect
|> Ordering.breakTiesWith (Ordering.byField (.numTries >> negate))
|> Ordering.breakTiesWith (Ordering.byField (.lastAskedAt >> dateToComparable))
|> Ordering.breakTiesWith
(Ordering.byField (.createdAt >> dateToComparable >> negate))
A more Elm-ish way of doing this kind of things is compositionally, thanks to the |> operator.
The elm-ordering library provides the primitives that you need to do this kind of things, especially the Ordering.byField and Ordering.breakTiesWith functions.
As for the dates, my advice would be to use Date.toTime (the resulting values are comparable).
Bonus: full implementation of your ordering function available for testing here: https://runelm.io/c/xoz. You can see it's much simpler and more readable than yours...
I am almost new with Erlang
I have a list as:
List = [[{name, <<"redCar1">>}, {turbo, true}], [{name, <<"redCar2">>}, {turbo, true}], [{name, <<"greenCard">>}, {turbo, false}]].
Now I want to filter all "red" Cars
I tried using:
filterCar() ->
MyF = fun(List) ->
case lists:keyfind(name, 1, List) of
{name, <<"red", _Rest/binary>>} ->
true:
_ ->
false
end
end,
MyF.
Then
lists:filter(MyF, List),
It works perfectly.
Now I want to create an generic function to filter, like:
myfilter(Value, List) ->
case lists:keyfind(name, 1, List) of
{name, <<Value, _Rest/binary>>} ->
true;
_ ->
false
end.
But when I try to execute this function I got always [] empty list.
I am sure the problem is when I try to pass Value because if I replace
{name, <<Value, _Rest/binary>>}
with
{name, <<"red", _Rest/binary>>}
It works.
My aim it to find all string that start with car in ignore case.
You just need to indicate two more things to use a general value in your binary: that it's a binary, and the size of that binary.
filterCar(Value) when is_binary(Value) ->
MyF = fun(List) ->
Size = byte_size(Value),
case lists:keyfind(name, 1, List) of
{name, <<Value:Size/binary, _Rest/binary>>} ->
true;
_ ->
false
end
end,
MyF.
First we changed filterGuard to take one argument, Value, which is the pattern we want to look for. We use a guard on the function to ensure Value is a binary. Inside the internal fun we first retrieve the size of Value via byte_size/1, which we need so that we can set the expected field size in the matching binary. This leads to the key change, which is <<Value:Size/binary, _Rest/binary>>: we set the expected size of the Value field, and we define it as a binary field.
With this change in place, we can successfully apply it to your List variable, passing <<"red">> for Value:
1> lists:filter(filterCar(<<"red">>), List).
[[{name,<<"redCar1">>},{turbo,true}],
[{name,<<"redCar2">>},{turbo,true}]]
PropertyInfo = [
[{LandNo, Acquisition, Heir, Property, LandTypeCount, LandType}],
[{LandNo, Acquisition, Heir, Property, LandTypeCount, LandType}],
[{LandNo, Acquisition, Heir, Property, LandTypeCount, LandType}],
[{LandNo, Acquisition, Heir, Property, LandTypeCount, LandType}],
]
PropertyInfo is a list of lists containing database objects in tuple, where Heir:code() will return a 6-digit code eg. "010011", "00209", ""020011".
How can I sort this list in erlang by using that Heir code?
By using lists:sort/2 and an ordering function:
manual excerpt:
sort(Fun, List1) -> List2
Types:
Fun = fun((A :: T, B :: T) -> boolean())
List1 = List2 = [T]
T => term()
Returns a list containing the sorted elements of List1,
according to the ordering function Fun. Fun(A, B) should return true
if A compares less than or equal to B in the ordering, false
otherwise.
Ordering fun could look like this:
fun([Tuple1],[Tuple2]) ->
Prop1 = element(3,Tuple1);
Prop2 = element(3,Tuple2);
case {Prop1:code(),Prop2:code()} of
{Same,Same} -> true;
{Code1,Code2} -> SomeComparisonFun(Code1,Code2)
end
end
This leaves you to provide a function that can compare those values, once you've decided on a metric that let's you say which value should be greater than some other one.
SortedList = lists:sort(
fun({_, _, A, _, _, _}, {_, _, B, _, _, _}) ->
A:code() =< B:code()
end,
PropertyInfo).
This was very simple, I used this inbuilt lists:sort() function.
I'd like to know if there is a function in Erlang can help me know whether an element is in a tuple or not. Like sets:is_element/2.
Tuple = {aaa,bbb,ccc}.
is_element_of_tuple(Tuple, aaa) % => true
is_element_of_tuple(Tuple, ddd) % => false
You can always transform the tuple to a list using tuple_to_list/1:
is_element_of_tuple(Tuple, Element) ->
lists:member(Element, tuple_to_list(Tuple)).
The simple answer is: no there is no function to do this. You have to write your own loop which traverses all the elements of a tuple until it either finds or does not find it. You an either convert the tuple to a list as above or write your own loop, something like:
is_element_of_tuple(E, Tuple) ->
is_element_of_tuple(E, Tuple, 1, tuple_size(Tuple)).
is_element_of_tuple(E, T, I, S) when I =< S ->
case element(I, T) of
E -> true;
_ -> is_element_of_tuple(E, T, I+1, S)
end;
is_element_of_tuple(_, _, _, _) -> false. %Done all the elements
Using a case and matching in this way means we check for exact equality, and it is probably a little faster than using =:= and checking if that returns true or false.