How to emulate scanf in dart?
I want to translate the following C code into dart.
#include <stdio.h>
void main() {
double a,b;
printf("a b? ");
scanf("%lf%lf",&a,&b);
printf("a=%lf b=%lf\n",a,b);
}
As I know, I cannot use call by reference, variable number arguments function call or destructuring assignment in dart.
So, it seems that it is impossible to make a function emulating scanf for now.
Here is my version in dart.
import "dart:io";
void main() {
stdout.write("a b? ");
var line = stdin.readLineSync();
var tokens = line?.split(RegExp(r'\s+'));
double a = double.tryParse(tokens?[0] ?? '0') ?? 0;
double b = double.tryParse(tokens?[1] ?? '0') ?? 0;
print("a=$a b=$b");
}
In there any possible improvement in the code?
Here is another version using Iterator.
import 'dart:io';
void main() {
stdout.write("a b? ");
var scan = Scan();
var a = scan.getDouble();
var b = scan.getInt();
print("a=$a b=$b");
}
class Scan {
Iterator? it;
Scan([String? line]) {
it = (line ?? stdin.readLineSync())?.split(RegExp(r'\s+')).iterator;
}
double getDouble() {
return double.tryParse(it?.moveNext() == true ? it?.current : '') ?? 0;
}
int getInt() {
return int.tryParse(it?.moveNext() == true ? it?.current : '') ?? 0;
}
}
Related
The listener function can listen to any parameter type(not only listener type). This has nothing related to widgets (this should work successfully in https://dartpad.dev without using the flutter).
ex.
int a = 0;
listener((a>0)=>print("A = $a"));
a= 1; //A = 1
a= -1; //
a= 2; //A = 2
You can use ValueNotifier for this. It's a ChangeNotifier that is triggered when the value is replaced with something that is not equal to the old value as evaluated by the equality operator ==.
Here is a nice tutorial about this approach.
The basic method is to create the function for updating the parameter that you want to add to the listener.
void test() {
int a = 0;
void updateA(newA) {
if(newA is! int) return;
a = newA;
if (a > 0) print("A = $a");
}
updateA(1);
updateA(-1);
updateA(2);
}
A better way is to create parameters with class.
void main() {
ParameterWithListener a = ParameterWithListener(data: 0);
a.listener = () {
if (a.data is int && a.data > 0) print("A = ${a.data}");
};
a.update(1);
a.update(-1);
a.update(2);
}
class ParameterWithListener {
ParameterWithListener({this.data, this.listener});
dynamic data;
Function()? listener;
Future update(data) async {
this.data = data;
if (listener is Function()) await listener!();
}
}
result:
A = 1
A = 2
here is my code
import 'dart:io';
import 'dart:math';
void main() {
bool flag = false;
for (int i = 0; i < 100; i++) {
gameCode();
if (userNumber == computerNumber) {
flag = true;
break;
}
}
}
int randomNumber(number) {
Random randNumber = Random();
int random = randNumber.nextInt(number);
return random;
}
gameCode() {
int computerNumber = randomNumber(9);
print("start guessing the number : ");
int userNumber = int.parse(stdin.readLineSync()!);
if (userNumber == computerNumber) {
print("You got it");
}
}
in this code you can see gameCode function. in that function there is two value that i need to use in main function.so how do i return those two keyword from that function ?
//userNumber // computerNumber
this is the variable that i want to return from that code
Dart not support return multiple values in function, you can return it with array, map, or you can use third lib tuple
Dart does not support returning multiple values in the current latest version. I would in your case recommend creating a class for the specific purpose of define the result from gameCode(). So something like this:
class GameCodeResult {
int userNumber;
int computerNumber;
GameCodeResult({
required this.userNumber,
required this.computerNumber,
});
}
Which we can then use like this in your program:
import 'dart:io';
import 'dart:math';
void main() {
bool flag = false;
for (int i = 0; i < 100; i++) {
GameCodeResult result = gameCode();
if (result.userNumber == result.computerNumber) {
flag = true;
break;
}
}
}
final _random = Random();
int randomNumber(int maxNumber) => _random.nextInt(maxNumber);
GameCodeResult gameCode() {
int computerNumber = randomNumber(9);
print("start guessing the number : ");
int userNumber = int.parse(stdin.readLineSync()!);
if (userNumber == computerNumber) {
print("You got it");
}
return GameCodeResult(userNumber: userNumber, computerNumber: computerNumber);
}
Note, I also fixed your randomNumber() method since it is not recommended to generate a new Random() object for each new random number you want. We should instead reuse an instance of Random in our program.
Please refer to below code
import 'dart:io';
import 'dart:math';
void main() {
bool flag = false;
for (int i = 0; i < 100; i++) {
Map<String, dynamic> res = gameCode();
print(res);
if (res['user_number'] == res['computer_number']) {
flag = true;
break;
}
}
}
int randomNumber(number) {
Random randNumber = Random();
int random = randNumber.nextInt(number);
return random;
}
Map<String, dynamic> gameCode() {
int computerNumber = randomNumber(9);
print("start guessing the number : ");
int userNumber =
int.parse(stdin.readLineSync()!);
if (userNumber == computerNumber) {
print("You got it");
}
return {
"computer_number": computerNumber,
"user_number": userNumber,
};
}
I know how to print map using foreach() method
var x = {1:'One',2:'Two',3:'Three',4:'Four',5:'Five'};
x.foreach((i,j){
print(i);
print(j);
});
and using normal for loop
other methods to print map ?
You can also directly print the map:
void main() {
var x = {1:'One',2:'Two',3:'Three',4:'Four',5:'Five'};
print(x);
}
The result will be pretty neat:
{1: One, 2: Two, 3: Three, 4: Four, 5: Five}
You will find the implementation of mapToString here.
static String mapToString(Map<Object?, Object?> m) {
// Reuses the list in IterableBase for detecting toString cycles.
if (_isToStringVisiting(m)) {
return '{...}';
}
var result = StringBuffer();
try {
_toStringVisiting.add(m);
result.write('{');
bool first = true;
m.forEach((Object? k, Object? v) {
if (!first) {
result.write(', ');
}
first = false;
result.write(k);
result.write(': ');
result.write(v);
});
result.write('}');
} finally {
assert(identical(_toStringVisiting.last, m));
_toStringVisiting.removeLast();
}
return result.toString();
}
I got the following error:
[error][1] (E/DartVM (14988): Exhausted heap space, trying to allocate 536870928 bytes.
E/flutter (14988): [ERROR:flutter/lib/ui/ui_dart_state.cc(148)] Unhandled Exception: Out of Memory)
When trying to implement a breadth_first search algorithm to find the shortest path in a graph. I found the algorithm written in C# and I am trying to rewrite it in dart/flutter.
The original code in C# can be found here.
My dart code:
import 'dart:collection';
import 'package:stack/stack.dart';
class Node<T>{
int id;
Node(this.id);
String toString() => '$id';
}
class Graph<T>{
final Map<Node, List<Node>> adj;
Graph(this.adj);
void AddEdge(Node node1,Node node2){
if(!adj.containsKey(node1))
adj[node1]=List<Node>();
if(!adj.containsKey(node2))
adj[node2]=List<Node>();
adj[node1].add(node2);
adj[node2].add(node1);
}
Stack<Node> ShortestPath(Node source, Node dest){
var path=Map<Node<T>,Node<T>>();
var distance=Map<Node<T>,int>();
//adj.keys.forEach(( node) => distance[node]=-1);
for(var node in adj.keys){
distance[node]=-1;
}
distance[source]=0;
var q=Queue<Node<T>>();
q.add(source);
while(q.isNotEmpty){
var node=q.removeFirst();
for(var adjs in adj[node].where((n) => distance[n]==-1)){
distance[adjs]=distance[node]+1;
path[adjs]=node;
q.add(adjs);
}
}
var res=Stack<Node>();
var cur=dest;
while(cur != res){
res.push(cur);
cur=path[cur];
}
res.push(source);
return res;
}
}
void main() {
var g = new Graph({});
var n1 = new Node<int>(1);
var n2 = new Node<int>(2);
var n3 = new Node<int>(3);
var n4 = new Node<int>(4);
var n5 = new Node<int>(5);
var n6 = new Node<int>(6);
var n7 = new Node<int>(7);
g.AddEdge(n1, n2);
g.AddEdge(n1, n3);
g.AddEdge(n1, n4);
g.AddEdge(n4, n5);
g.AddEdge(n2, n6);
g.AddEdge(n4, n7);
g.AddEdge(n5, n6);
g.AddEdge(n6, n7);
var answ=g.ShortestPath(n1, n7);
print(answ);
}
So what is the wrong with my program, and if anyone know better way to find shortest path in graph to use it in dart it will be great.
Thanks in advance
First, you main problem is properly that your while loop is not correct according to the C# implementation:
var res=Stack<Node>();
var cur=dest;
while(cur != res){
res.push(cur);
cur=path[cur];
}
res.push(source);
return res;
This loop will never finish res and cur are entirely different types where cur are a Node and res are a Stack. If you check the C# implementation you can see this is not correct:
var res = new Stack<Node<T>>();
var cur = dest;
while(cur != source) {
res.Push(cur);
cur = path[cur];
}
res.Push(source);
return res;
So I think by comparing against source it will properly solve the problem. But there are a lot of smaller problems in you code where types are not really great and where you could make it a lot more type safe by using generics more places.
I have therefore added more typing information to you code (which I needed to do to catch the type error). I have also dropped the usage of the Stack class since I don't think it makes much sense. Also, the Stack class you got had no toString implementation so I just thought it was easier to just use a List and return that as the result:
import 'dart:collection';
class Node<T> {
int id;
Node(this.id);
#override
String toString() => '$id';
}
class Graph<T> {
final Map<Node<T>, List<Node<T>>> adj;
Graph(this.adj);
void AddEdge(Node<T> node1, Node<T> node2) {
if (!adj.containsKey(node1)) adj[node1] = <Node<T>>[];
if (!adj.containsKey(node2)) adj[node2] = <Node<T>>[];
adj[node1].add(node2);
adj[node2].add(node1);
}
List<Node<T>> ShortestPath(Node<T> source, Node<T> dest) {
final path = <Node<T>, Node<T>>{};
final distance = <Node<T>, int>{};
//adj.keys.forEach(( node) => distance[node]=-1);
for (final node in adj.keys) {
distance[node] = -1;
}
distance[source] = 0;
final q = Queue<Node<T>>();
q.add(source);
while (q.isNotEmpty) {
final node = q.removeFirst();
for (final adjs in adj[node].where((n) => distance[n] == -1)) {
distance[adjs] = distance[node] + 1;
path[adjs] = node;
q.add(adjs);
}
}
final res = <Node<T>>[];
var cur = dest;
while (cur != source) {
res.add(cur);
cur = path[cur];
}
res.add(source);
return res;
}
}
void main() {
final g = Graph<int>({});
final n1 = Node<int>(1);
final n2 = Node<int>(2);
final n3 = Node<int>(3);
final n4 = Node<int>(4);
final n5 = Node<int>(5);
final n6 = Node<int>(6);
final n7 = Node<int>(7);
g.AddEdge(n1, n2);
g.AddEdge(n1, n3);
g.AddEdge(n1, n4);
g.AddEdge(n4, n5);
g.AddEdge(n2, n6);
g.AddEdge(n4, n7);
g.AddEdge(n5, n6);
g.AddEdge(n6, n7);
final answ = g.ShortestPath(n1, n7);
print(answ); // [7, 4, 1]
}
This question is a continuation of a previous question. I wrote the following piece of code to determine if File.openRead() created a Stream that could be streamed line-by-line. It turns out that the answer is no. The entire file is read and then passed to the next transform. My question is then: How do you Stream a file line-by-line in Dart?
import 'dart:async';
import 'dart:convert';
import 'dart:io';
void main(List<String> arguments) {
Stream<List<int>> stream = new File('Data.txt').openRead();
stream
.transform(const Utf8InterceptDecoder())
.transform(const LineSplitterIntercept())
.listen((line) {
// stdout.writeln(line);
}).asFuture().catchError((_) => print(_));
}
int lineSplitCount = 0;
class LineSplitterIntercept extends LineSplitter {
const LineSplitterIntercept() : super();
// Never gets called
List<String> convert(String data) {
stdout.writeln("LineSplitterIntercept.convert : Data:" + data);
return super.convert(data);
}
StringConversionSink startChunkedConversion(ChunkedConversionSink<String> sink) {
stdout.writeln("LineSplitterIntercept.startChunkedConversion Count:"+lineSplitCount.toString()+ " Sink: " + sink.toString());
lineSplitCount++;
return super.startChunkedConversion(sink);
}
}
int utfCount = 0;
class Utf8InterceptDecoder extends Utf8Decoder {
const Utf8InterceptDecoder() : super();
//never gets called
String convert(List<int> codeUnits) {
stdout.writeln("Utf8InterceptDecoder.convert : codeUnits.length:" + codeUnits.length.toString());
return super.convert(codeUnits);
}
ByteConversionSink startChunkedConversion(ChunkedConversionSink<String> sink) {
stdout.writeln("Utf8InterceptDecoder.startChunkedConversion Count:"+ utfCount.toString() + " Sink: "+ sink.toString());
utfCount++;
return super.startChunkedConversion(sink);
}
}
I think this code is useful:
import 'dart:io';
import 'dart:convert';
import 'dart:async';
main() {
final file = new File('file.txt');
Stream<List<int>> inputStream = file.openRead();
inputStream
.transform(utf8.decoder) // Decode bytes to UTF-8.
.transform(new LineSplitter()) // Convert stream to individual lines.
.listen((String line) { // Process results.
print('$line: ${line.length} bytes');
},
onDone: () { print('File is now closed.'); },
onError: (e) { print(e.toString()); });
}
If a stream is necessary, you can create it from the future that readAsLines() returns:
Stream<List<String>> stream =
new Stream.fromFuture(new File('Data.txt').readAsLines());
However it looks simpler to me to plainly process the lines one by one,
List<String> lines = new File('Data.txt').readAsLinesSync();
for (var line in lines) {
stdout.writeln(line);
}
The converter's startChunkedConversion is only called once, when the transformation is started. However, the returned sink's add method is invoked multiple times with parts of the file.
It's up to the source to decide how big the chunks are, but a 37MB file (as mentioned in your previous question) will definitely be sent in smaller chunks.
If you want to see the chunks you can either intercept startChunkedConversion and return a wrapped sink, or you can put yourself between the openRead and the transformer.
Intercept:
class InterceptSink {
static int lineSplitCount = 0;
final _sink;
InterceptSink(this._sink);
add(x) {
print("InterceptSink.add Count: $lineSplitCount");
lineSplitCount++;
_sink.add(x);
}
close() { _sink.close(); }
}
class LineSplitterIntercept extends Converter {
convert(x) { throw "unimplemented"; }
startChunkedConversion(outSink) {
var lineSink = new LineSplitter().startChunkedConversion(outSink);
return new InterceptSink(lineSink);
}
}
After openRead:
file.openRead()
.transform(UTF8.decoder)
.map(x) {
print("chunk size: ${x.length)");
return x;
}
.transform(new LineSplitter())
...
Because none of the other answers suited my situation, here is another technique:
import 'dart:io';
import 'dart:convert';
void main()
{
var file = File('/path/to/some/file.txt');
var raf = file.openSync(mode: fileMode.read);
String line;
while ((line = readLine(raf)) != null)
{
print(line);
}
}
String readLine(RandomAccessFile raf, {String lineDelimiter = '\n'}) {
var line = '';
int byte;
var priorChar = '';
var foundDelimiter = false;
while ((byte = raf.readByteSync()) != -1) {
var char = utf8.decode([byte]);
if (isLineDelimiter(priorChar, char, lineDelimiter)) {
foundDelimiter = true;
break;
}
line += char;
priorChar = char;
}
if (line.isEmpty && foundDelimiter == false) {
line = null;
}
return line;
}
bool isLineDelimiter(String priorChar, String char, String lineDelimiter) {
if (lineDelimiter.length == 1) {
return char == lineDelimiter;
} else {
return priorChar + char == lineDelimiter;
}
}
Adjusting Brett Sutton's answer for sound null safety and wider availability:
import 'dart:io';
import 'dart:convert';
bool isLineDelimiter(String priorChar, String char, String lineDelimiter)
{
if (lineDelimiter.length == 1) {
return char == lineDelimiter;
} else {
return priorChar + char == lineDelimiter;
}
}
/// Reads one line and returns its contents.
///
/// If end-of-file has been reached and the line is empty null is returned.
String? readLine(RandomAccessFile raf,
{String lineDelimiter = '\n', void Function()? onEOF}) {
String line = '';
int byte;
String priorChar = '';
byte = raf.readByteSync();
while (byte != -1) {
String char = utf8.decode([byte]);
if (isLineDelimiter(priorChar, char, lineDelimiter)) return line;
line += char;
priorChar = char;
byte = raf.readByteSync();
}
onEOF?.call();
if (line.isEmpty) return null;
return line;
}
EDIT 1:
I wanted to add some more line-specific functions I made:
/// Skips one line and returns the last byte read.
///
/// If end-of-file has been reached -1 is returned.
int skipLine(RandomAccessFile raf,
{String lineDelimiter = '\n', void Function()? onEOF}) {
int byte;
String priorChar = '';
byte = raf.readByteSync();
while (byte != -1) {
String char = utf8.decode([byte]);
if (isLineDelimiter(priorChar, char, lineDelimiter)) return byte;
priorChar = char;
byte = raf.readByteSync();
}
return byte;
}
/// Reads all lines in the file and executes [onLine] per each.
///
/// If [onLine] returns true the function terminates.
void processLines(
RandomAccessFile raf, {
String lineDelimiter = '\n',
required bool? Function(String line, bool eofReached) onLine,
}) {
bool _eofReached = false;
do {
String? _line;
_line = readLine(raf,
lineDelimiter: lineDelimiter, onEOF: () => _eofReached = true);
if (_line == null) return;
if (onLine(_line, _eofReached) == true) return;
} while (!_eofReached);
}