string format in Scala - scala

New to Scala and see people are using sign f ahead of a string, here is an example I tried which works. Wondering what is the function of sign f? Does it need to be combined to use with %s? Tried to search some tutorials but failed. Thanks.
object HelloWorld {
def main(args: Array[String]) {
var start = "Monday";
var end = "Friday";
var palindrome = "Dot saw I was Tod";
println(f"date >= $start%s and date <= $end%s" + palindrome);
// output date >= Monday and date <= FridayDot saw I was Tod
}
}

http://docs.scala-lang.org/overviews/core/string-interpolation.html
The f Interpolator
Prepending f to any string literal allows the creation of simple
formatted strings, similar to printf in other languages. When using
the f interpolator, all variable references should be followed by a
printf-style format string, like %d.
PS. another somewhat related feature is http://docs.scala-lang.org/overviews/quasiquotes/expression-details

See the explanation here. For people coming from C the f interpolator is a printf style formatter. % is to denote the type of data and with a $ you may may refer to a previously defined variable.
The % in not mandatory. Its just that you will get a format that is decided by the compiler at compile time. Bit uyou may want to change the output format sometimes.
So if i take an example ,
var start = "Monday";
var end = "Friday";
val age = 33
var palindrome = "Dot saw I was Tod";
println(f"date >= $start and date <= $end and age<= $age%f" + palindrome);
I could omit the %f and i will see a output of 33 as it will inferred as Int. However i could use %f if i wanted to format it as a float. Also if you use a incompatible formatted you will receive a error at compile time.

Related

How to use String.format() in scala

I am new to Scala world, I wanted to use String.format() to create a date format string.
I have three integer value year month and day, I wanted to change it in yyyy-mm-dd. String.format() expect an array of Anyref, when I am creating Array[Anyref] by passing integer value to it, it is throwing below error.
Error:(49, 30) the result type of an implicit conversion must be more specific than AnyRef
dd(2) = inputCalendar.get(5)
My full example is :
val dd = new Array[AnyRef](3);
dd(0) = Integer.valueOf(inputCalendar.get(1))
dd(1) = Integer.valueOf(inputCalendar.get(2) + 1)
dd(2) = inputCalendar.get(5)
println(String.format("%04d-%02d-%02d",dd))
Note: I don't want to use any Date API for this.
Declare dd elements as type Int and this should work.
val dd = new Array[Int](3)
. . . //unchanged
String.format("%04d-%02d-%02d",dd:_*)
Or ...
"%04d-%02d-%02d".format(dd:_*)

How to make use of the unit attribute within a model in Modelica?

Motivation
Modelica does store units of measurement (e.g. SI units and Non-SI units) as an attribute with regard to a variable. Here is an example for a Non-SI-unit:
type Time_months = Real( quantity = "Time", unit = "mo", displayUnit = "months" )
Since for models in economics it will be rather akward to give rates in seconds, I would like to write a rather general unit conversion function that will allow to convert units of time. So ideally a function to convert to another time base should work with three inputs and one output:
input Real timeValue "the value of time to be converted";
input String timeBaseA "the time base for timeValue, e.g. \"mo\" ";
input String timeBaseB "the time base to convert to, e.g. \"yr\" ";
output Real convertedTimeValue "the result of the conversion";
Questions
If we assume that a variable for some time value already has a specific unit attribute (e.g. "mo") it would make sense to use that meta information within a model.
Question 1: How can meta information like unit be accessed within a model?
Ideally something like the following would be great:
String timeBaseA := timeValue.unit;
or
String timeBaseA := getUnit( timeValue ) "some function to read unit information";
Question 2: How can meta information like unit be assigned within a function?
In the example we would of course like to return the output value with the correct unit of time. So ideally we would like to have:
output Real convertedTime( quantity = "Time", unit = strTimeBaseB )
Unfortunately, using an input will give rise to an error as the variability is different: The unit attribute should have constant variability but the input variable has parameter variability. (Using a function - which would be nice - also fails for the same reason.)
Regarding Question 1:
I have never used Wolfram SystemModeler, but the Modelica Language Specification 3.4 says in chapter 4.8 (Predefined Types and Classes):
The attributes of the predefined variable types (Real, Integer, Boolean, String) ... cannot be accessed using dot notation, and are not constrained by equations and algorithm sections.
Regarding Question 2:
I think it is only possible to define the unit of a variable on declaration from a literal or from a final parameter - at least this is what I observed in Dymola.
Alternative - use operator records
You could use operator records for your task. This will allow you to store the time in seconds and convert it to what ever needed when the value comes to use.
Operator records allow you to define several function to create them, compare or add them, convert to String, etc.
See the brief example below, where a operator record Time is defined, which can be created with two different constructor functions from seconds or days and can be converted to Strings with day or seconds
operator record Time
Integer s "Second";
encapsulated operator 'constructor'
import Time;
function from_s
input Integer s "Seconds";
output Time t(s=s);
algorithm
end from_s;
function from_d
input Integer d "Days";
output Time t(s=d*24*3600);
algorithm
end from_d;
end 'constructor';
encapsulated operator 'String' "Convert Time to string"
import Time;
function formated
input Time t;
input String format = "s" annotation(choices(choice="s" "seconds", choice="d" "days"));
output String str;
algorithm
if format == "d" then
str :=String(t.s/24/3600);
else
str :=String(t.s);
end if;
end formated;
end 'String';
encapsulated operator function '==' "Compare time records"
import Time;
input Time t1;
input Time t2;
output Boolean result "= t1 == t2";
algorithm
result := t1.s == t2.s;
end '==';
end Time;
Usage:
import Modelica.Utilities.Streams.print
t1 = Time(d=12) // create record using day constructor
t2 = Time(s=3600*24*2) // create record using second constructor
print(String(t1, format="s")) // prints 1036800
print(String(t1, format="d")) // prints 12
print(String(t2, format="s")) // prints 172800
print(String(t2, format="d")) // prints 2
See Modelica Spec 3.4 Chapter 14 "Overloaded Operators" for details.
Note: This was tested with Dymola 2019, not with Wolfram SystemModeler
In Modelica usually every variable is computed based on SI units. Then you have displayUnits to plot them in a different unit (not affecting the actual computation).
I don't know about SystemModeler, but in Dymola the conversion between the unit (of computation) and the displayUnit (only for plotting) is handled by a pre-defined script (displayUnit.mos). It can be extended by the user to contain custom displayUnits. The code for the display units related to time is shown below. I extended it to have week (w) additionally to the predefined ones.
// Syntax:
// defineUnitConversion(<unit>, <derived unit>, <scale>, <opt. offset>);
// Time
defineUnitConversion("s", "ms", 1000);
defineUnitConversion("s", "min", 1/60);
defineUnitConversion("s", "h", 1/3600);
defineUnitConversion("s", "d", 1/86400);
defineUnitConversion("s", "w", 1/604800);
This can then be selected in plots manually or as the default ´displayUnit´ via Modelica.SIunits.Time t(displayUnit = "w") = ...;
The disadvantage is, that this extension has to be done in a file in the install directory. So it has to be changed again after re-installing the tool or when using a different computer.
If there are numerical reasons to not compute solutions in seconds (e.g. because values would get to big), the solution would be the nominal attribute, which enables a scaling of the variables.
BTW: I think months are not a very good unit of time as they can have 28 to 31 days. That's why I chose weeks in my example.
You could use conversion like is done in the MSL, for example the function Modelica.SIunits.Conversions.to_degC which has the signature:
function to_degC
input Temperature Kelvin "Kelvin value";
output NonSIunits.Temperature_degC Celsius "Celsius value";
end to_degC;
This works, but you need one such function for each unit you want to convert between (which is why most calculations are done using SI-units).

number_in_month exercise

I am fresh on SML and doing a homework by that. "Write a function number_in_month that takes a list of dates and a month (i.e., an int) and returns how many dates in the list are in the given month."
That's what I worked out and cannot see anything wrong with it. Please help.
`
fun number_in_month (dates: (int*int*int) list,month:int) =
if ((#2 (hd dates)) = month)
then val flag=1 flag+number_in_month(tl dates, month)
else number_in_month((tl dates),month)`
REPL tells that: replacing VAL with EQUALOP.
You can't bind variables "that way". A binding of a variable is a declaration and thus cannot be done where an expression is expected.
In this case you have to use a let-in-end expression
fun foo x =
let
val a = 42
in
a*x
end
Your problem is endless recursion. Compiler can't get out of it because independ of result if..then..else you're running your function again
Try this:
fun number_in_month (dates: (int*int*int) list,month:int) =
if null dates
then 0
else if ((#2 (hd dates)) = month)
then val flag=1 flag+number_in_month(tl dates, month)
else number_in_month((tl dates),month)
I tried to fix it by myself and that was my solution:
fun number_in_month (dias: (int*int*int) list,mes:int) =
if null dias
then 0
else if ((#2 (hd dias)) = mes)
then let val flag = 1 + number_in_month(tl dias, mes)
in flag
end
else number_in_month((tl dias),mes)
I hope you can also use it!
The error message from REPL is confusing, but Jesper is right that you should use let-in-end expression, if you need assignment statement in functions. That will surely get you that error resolved.

datenum conversion failed, string format is 'QQ-YYYY'

I have a fairly simple question: I have a cell vector that looks like this:
temp_y_date{1} = '2012Q2'
temp_y_date{2} = '2012Q1'
temp_y_date{3} = '2011Q4'
I would like to transform this cell vector into a date vector using the function datenum. I initialy transform the vector to the format 'QQ-YYYY' as follows:
for i = 1:length(temp_y_date)
temp = temp_y_date(i);
year = cellfun(#(c) {c(1:4)}, temp);
quarter = cellfun(#(c) {c(5:6)}, temp);
temp_y_date(i) = strcat(quarter,'-',year);
end
The values of temp_y_date are now
temp_y_date (1) = 'Q2-2012'
temp_y_date (2) = 'Q1-2012'
temp_y_date (3) = 'Q4-2011'
I thought I could now apply the datenum function:
temp_y_date = datenum(temp_y_date,'QQ-YYYY');
However, I get the error:
??? Error using ==> datenum at 178
DATENUM failed.
Caused by:
Error using ==> dtstr2dtnummx
Failed on converting date string to date number.
I tested on both R2009b and R2012a. Your code works in the latter, but I get the same error in R2009b.
When I dropped the Q in temp_y_date it was fixed in the older version. So apparently older versions don't accept the a quarter definition.
Working code:
strdate = {'2012Q2', '2012Q1', '2011Q4'};
strdate2 = cellfun(#(c) strcat(c(6),'-',c(1:4)),strdate,'uni',false);
result = datenum(strdate2,'QQ-YYYY');
I changed you're variable names, so it's easier for debugging. Also fixed the loop with cellfun in it, you were using it quite inefficiently. I suggest you learn what a cell is, and how to index it.
Note: this will break if you upgrade to another version, or if someone else uses it in a later version. So if you don't want that, I suggest you use verLessThan to distinguish between different versions of matlab:
EDIT:
From help datenum:
Formats with 'Q' are not accepted by DATENUM.
So I guess you'll have to either upgrade or implement this one yourself, doesn't look that hard though:
strdate = {'2012Q2', '2012Q1', '2011Q4'};
if verLessThan('matlab','7.12') % or maybe 7.11, it depends on which version the datenum functionality changed...
strdate2 = cellfun(#(c) sprintf('%s/%02d/01',c(1:4),3*str2double(c(6))-2),strdate,'uni',false);
result = datenum(strdate2,'YYYY/mm/dd');
else
strdate2 = cellfun(#(c) strcat(c(5:6),'-',c(1:4)),strdate,'uni',false);
result = datenum(strdate2,'QQ-YYYY');
end
So as you can see, older versions of datenum need full date spec: 'YYYY/mm/dd'.

how to make matlab detect incompatible type assignments?

hi have a major problem in matlab. I have a function and it sometimes returns control ascii characters. How do i check for the presence of these control ascii ?.
my code looks like this
d = out.autoc
d sometimes receives control ascii characters instead of a actual double value. Does someone know how to catch such incompatible assignments ?
I think this should work but you may want to double check the ASCII codes to exclude.
%here I load Ctrl-C
s = sprintf('%s', 3);
code = bin2dec(dec2bin(s,8));
if code < 32
fprintf('ignore');
else
fprintf('do somsething');
end
If you want to check that the value of d is double, and not a string. You can check it this way:
if ~isnumeric(d) || ~isdouble(d)
fprintf('d is not of class double\n');
end
But if you want to assign the value of out.autoc to d only if out.autoc is double, you can do this:
if isnumeric(out.autoc) && isequal(class(out.autoc), 'double')
d = out.autoc;
else
fprintf('out.autoc is not of class double, no assignment made.\n');
end