How to make a search in filesystem-like structure in Haskell - file

problem
I have a Type:
data FSObject = Folder String [FSObject] | File String
and I need to make a function
search :: String -> FSObject -> Maybe String
that returns a path to File that we are searching for (if exists)
(String in search function should be equal to the name (the String in File object (w/o path)) of the searched File.
my thoughts/tryings
I feel like I am not doing it properly- in a functional way. I am new to this language so I am sorry for the following code.
I was trying to do this for several hours. I was trying like this:
heler function contains that returns true if given FSObject contains File that we are looking for
helper function that returns first element with the File (using previous function and "find" function)
helper function to deal with conversion from Maybe String to String
My search function would check if there is the File, if no-> return Nothing, else return Just with String computed somehow using recursion
I can paste my work but I don't know how to make it work, its totally unreadable etc.
Do some1 has hints/comments to share? How to properly deal with Maybe in this kind of problems?

You can do this using recursion.
findPath::String->FSObject->Maybe String
findPath name (File name') | name == name' = Just name
findPath _ (File _) = Nothing
findPath name (Folder path contents) =
fmap ((path ++ "/") ++) $ msum $ map (findPath name) contents
The only tricky point is the last part. Breaking the last line apart....
map (findPath name) contents
will output a list of subresults
[Just "a/b/filename", Nothing, Just "c/d/filename", ....]
msum will take the first Just in the list, throwing the rest away (and, by laziness, nothing further than that first value will actually be created)
Finally, the path is prepended (the fmap is there to reach inside the Maybe)

While #jamshidh solution is short, it isn't modular and the final result. Here is my process of writing the program. Two main points:
I'll use so called "generate then search" approach: first I generate paths for all files, then I search the collection for the right path
I'll store path as list of components - this way the code will be more generic (in Haskell more generic code is less error-prone) and I will insert path separators afterwards in a small function (so I can do one thing at a time which is easier).
Ok I need function allPaths that gives me list of all files along with their paths. All paths of a single file is that single file, and all paths of a folder is concatenated collections of paths from children with prepended folder name:
allPaths (File file) = singleFile file
allPaths (Folder folder children) = concatMap (addFolder folder . allPaths) children
I actually wrote the code top-down. So I didn't bother defining singleFile and addFolder at this point. Single file is simple:
singleFile file = [(file, [])]
addFolder adds f to second component of a tuple. There is a function in Control.Arrow for that already, but I add its implementation here for simplicity:
second f (a,x) = (a, f x)
addFolder f files = map (second (f:)) files
When I was learning Haskell it was hard to write such code at once, but now it's automatic and without intermediate steps.
Now we basically implement search by filtering all matching files, taking the first match and extracting the path. Oh, there is already function lookup in the standard library for that:
search fileToSearch l = lookup fileToSearch $ allPaths l
It took me quite a while to figure out how to compose lookup and allPaths. Fortunately the order in the tuple was chosen correctly by accident.
Note that you still need to convert folder list to a path by inserting separators and appending filename as necessary using concatMap.

You should create a recursive function using an accumulation parameter to save the current path where you're searching. It would be something like this:
search str (File f) = if str == f then Just str else Nothing
search str (Directory dir (x:xs)) =
Just (sear str [] (Directory dir (x:xs))) where
sear str path (File f) = if str == f then (path ++ "/" ++ str ++ "|") else []
sear str path (Directory y []) = []
sear str path (Directory dir (x:xs)) = sear str (path ++ "/" ++ dir) x ++ sear str path (Directory dir xs)
I hope it's helpful for you.

Related

How do I get a random line from a file?

I'm trying to get a random line from a file:
extern crate rand;
use rand::Rng;
use std::{
fs::File,
io::{prelude::*, BufReader},
};
const FILENAME: &str = "/etc/hosts";
fn find_word() -> String {
let f = File::open(FILENAME).expect(&format!("(;_;) file not found: {}", FILENAME));
let f = BufReader::new(f);
let lines: Vec<_> = f.lines().collect();
let n = rand::thread_rng().gen_range(0, lines.len());
let line = lines
.get(n)
.expect(&format!("(;_;) Couldn't get {}th line", n))
.unwrap_or(String::from(""));
line
}
This code doesn't work:
error[E0507]: cannot move out of borrowed content
--> src/main.rs:18:16
|
18 | let line = lines
| ________________^
19 | | .get(n)
20 | | .expect(&format!("(;_;) Couldn't get {}th line", n))
| |____________________________________________________________^ cannot move out of borrowed content
I tried adding .clone() before .expect(...) and before .unwrap_or(...) but it gave the same error.
Is there a better way to get a random line from a file that doesn't involve collecting the whole file in a Vec?
Use IteratorRandom::choose to randomly sample from an iterator using reservoir sampling. This will scan through the entire file once, creating Strings for each line, but it will not create a giant vector for every line:
use rand::seq::IteratorRandom; // 0.7.3
use std::{
fs::File,
io::{BufRead, BufReader},
};
const FILENAME: &str = "/etc/hosts";
fn find_word() -> String {
let f = File::open(FILENAME)
.unwrap_or_else(|e| panic!("(;_;) file not found: {}: {}", FILENAME, e));
let f = BufReader::new(f);
let lines = f.lines().map(|l| l.expect("Couldn't read line"));
lines
.choose(&mut rand::thread_rng())
.expect("File had no lines")
}
Your original problem is that:
slice::get returns an optional reference into the vector.
You can either clone this or take ownership of the value:
let line = lines[n].cloned()
let line = lines.swap_remove(n)
Both of these panic if n is out-of-bounds, which is reasonable here as you know that you are in bounds.
BufRead::lines returns io::Result<String>, so you have to handle that error case.
Additionally, don't use format! with expect:
expect(&format!("..."))
This will unconditionally allocate memory. When there's no failure, that allocation is wasted. Use unwrap_or_else as shown.
Is there a better way to get a random line from a file that doesn't involve collecting the whole file in a Vec?
You will always need to read the whole file, if only to know the number of lines. However, you don't need to store everything in memory, you can read lines one by one and discard them as you go so that you only keep one in the end. Here is how it goes:
Read and store the first line;
Read the second line, draw a random choice and either:
keep the first line with a probability of 50%,
or discard the first line and store the second line with a probability of 50%,
Keep reading lines from the file and for line number n, draw a random choice and:
keep the currently stored line with a probability of (n-1)/n,
or replace the currently stored line with the current line with a probability of 1/n.
Note that this is more or less what sample_iter does, except that sample_iter is more generic since it can work on any iterator and it can pick samples of any size (eg. it can choose k items randomly).

how to get a particular block in an array get copied in perl

I have details like below in an array. There will be plenty of testbed details in actual case. I want to grep a particular testbed(TESTBED = vApp_eprapot_icr) and an infomation like below should get copied to another array. How can I do it using perl ? End of Testbed info can be understood by a closing flower bracket }.
TESTBED = vApp_eprapot_icr {
DEVICE = vApp_eprapot_icr-ipos1
DEVICE = vApp_eprapot_icr-ipos2
DEVICE = vApp_eprapot_icr-ipos3
DEVICE = vApp_eprapot_icr-ipos5
CARDS=1GIGE,ETHFAST
CARDS=3GIGE,ETHFAST
CARDS=10PGIGE,ETHFAST
CARDS=20PGIGE,ETHFAST
CARDS=40PGIGE,ETHFAST
CARDS=ETHFAST,ETHFAST
CARDS=10GIGE,ETHFAST
CARDS=ETH,ETHFAST
CARDS=10P10GIGE,ETHFAST
CARDS=PPA2GIGE,ETHFAST
CARDS=ETH,ETHFAST,ETHGIGE
}
I will make it simpler, please see the below array
#array("
student=Amit {
Age=20
sex=male
rollno=201
}
student=Akshaya {
Age=24
phone:88665544
sex=female
rollno=407
}
student=Akash {
Age=23
sex=male
rollno=356
address=na
phone=88456789
}
");
Consider an array like this. Where such entries are plenty. I need to grep, for an example student=Akshaya's data. from the opening '{' to closing '}' all info should get copied to another array. This is what I'm looking for.
while (<>) {
print if /TESTBED = vApp_eprapot_icr/../\}/;
}
as a sidenote <> will capture the filename you use on cmdline. So if the data is stored in a file you will run from commandline
perl scriptname.pl filename.txt
Ok. We finally have enough information to come up with an answer. Or, at least, to produce two answers which will work on slightly different versions of your input file.
In a comment you say that you are creating your array like this:
#array = `cat $file`;
That's not a very good idea for a couple of reasons. Firstly, why run an external command like cat when Perl will read the file for you. And secondly, this gives you one element in your array for each line in your input file. Things become far easier if you arrange it so that each of your TESTBED = foo { ... } records is a single array element.
Let's get rid of the cat first. The easiest way to read a single file into an array is to use the file input operator - <>. That will read data from the file whose name is given on the command line. So if you call your program filter_records, you can call it like this:
$ ./filter_records your_input_data.txt
And then read it into an array like this:
#array = <>;
That's good, but we still have each line of the input file in its own array element. How we fix that depends on the exact format of your input file. It's easiest if there's a blank line between each record in the input file, so it looks like this:
student=Amit {
Age=20
sex=male
rollno=201
}
student=Akshaya {
Age=24
phone:88665544
sex=female
rollno=407
}
student=Akash {
Age=23
sex=male
rollno=356
address=na
phone=88456789
}
Perl has a special variable called $/ which controls how it reads records from input files. If we set it to be an empty string then Perl goes into "paragraph" mode and it uses blank lines to delimit records. So we can write code like this:
{
local $/ = '';
#array = <>;
}
Note that it's always a good idea to localise changes to Perl's special variables, which is why I have enclosed the whole thing in a naked block.
If there are no blank lines, then things get slightly harder. We'll read the whole file in and then split it.
Here's our example file with no blank lines:
student=Amit {
Age=20
sex=male
rollno=201
}
student=Akshaya {
Age=24
phone:88665544
sex=female
rollno=407
}
student=Akash {
Age=23
sex=male
rollno=356
address=na
phone=88456789
}
And here's the code we use to read that data into an array.
{
local $/;
$data = <>;
}
#array = split /(?<=^})\n/m, $data;
This time, we've set $/ to undef which means that all of the data has been read from the file. We then split the data wherever we find a newline that is preceded by a } on a line by itself.
Whichever of the two solutions above that we use, we end up with an array which (for our sample data) has three elements - one for each of the records in our data file. It's then simple to use Perl's grep to filter that array in various ways:
# All students whose names start with 'Ak'
#filtered_array = grep { /student=Ak/ } #array;
If you use similar techniques on your original data file, then you can get the records that you are interested in with code like this:
#filtered_array = grep { /TESTBED = vApp_eprapot_icr/ } #array;

Proper way to parse a file and build output

I'm trying to learn D and I thought after doing the hello world stuff, I could try something I wanted to do in Java before, where it was a big pain because of the way the Regex API worked: A little template engine.
So, I started with some simple code to read through a file, character by character:
import std.stdio, std.file, std.uni, std.array;
void main(string [] args) {
File f = File("src/res/test.dtl", "r");
bool escape = false;
char [] result;
Appender!(char[]) appender = appender(result);
foreach(c; f.rawRead(new char[f.size])) {
if(c == '\\') {
escape = true;
continue;
}
if(escape) {
escape = false;
// do something special
}
if(c == '#') {
// start of scope
}
appender.put(c);
}
writeln(appender.data());
}
The contents of my file could be something like this:
<h1>#{hello}</h1>
The goal is to replace the #{hello} part with some value passed to the engine.
So, I actually have two questions:
1. Is that a good way to process characters from file in D? I hacked this together after searching through all the imported modules and picking what sounded like it might do the job.
2. Sometimes, I would want to access more than one character (to improve checking for escape-sequences, find a whole scope, etc. Should I slice the array for that? Or are D's regex functions up to that challenge? So far, I only found matchFirst and matchAll methods, but I would like to match, replace and return to that position. How could that be done?
D standard library does not provide what you require. What you need is called "string interpolation", and here is a very nice implementation in D that you can use the way you describe: https://github.com/Abscissa/scriptlike/blob/4350eb745531720764861c82e0c4e689861bb17e/src/scriptlike/core.d#L139
Here is a blog post about this library: https://p0nce.github.io/d-idioms/#String-interpolation-as-a-library

Need to check whether any extension is present before query string

I have written the code to check the query string
Logic::if query string is "?" should remove all the characters from the query string and print the vailid URL.
char str[] = "http://john.org/test.mp4?iufjdlwle";
char *pch;
pch = strtok(str,"?");
printf("%s\n",pch);
Output::
bash-3.2$ ./querystring
http://john.com/test.mp4
But i have to check one more case
Need to get the URL only if there is any extensions present before query string?
if No extensions are present before the query string,need to skip.
I have tried this way,
continuation of the code
char *final;
final = pch+(strlen(pch)-3);
printf("%s\n",final);
if(strcasecmp(p,"mp4"))
printf("falure case\n");
else
printf("Success case\n");
It will work for .mp4 extension alone.
Incase if i'm getting *.mpeg or *.m3u8 or *.flv as an extensions,it will fail.
Can someone guide me how to solve this problem and make it working?
A query string is what starts after a question mark ?, fine.
You should try to define what an extension is. For me, it is what can happen after a dot (.) in the last component of the url, where the components are delimited with slashes (/)
So you should do:
first remove the possible query string including the initial ?
then locate the last /
then locate the last . that occurs after the last /
If you find one, it is the starting point of the extension.
So assuming pch contains the url without any query string, you can do:
char * ix = strrchr(pch, '/');
if (ix == NULL) {
// an URL without / is rather weird, better report and abort
...
}
ix = strrchr(ix, '.');
if (ix == NULL) {
// no extension here: ignore the url
...
}
else {
// found an URL containing an extension: process it
// ix+1 points to the extension
...
}

How to call an m file from another m file in MATLAB and retrieve an output?

I know this is a simple question, but for some reason I can't find a straight answer that works no matter where I look.
Basically, I have 4 values that were found in one m file, and I want to run them through a separate m file and retrieve the output from it.
I tried something like these, but none worked:
result = generate(nrow,ncol,a,b);
function result = generate(nrow,ncol,a,b);
result = #generate(nrow,ncol,a,b);
The final value in the m file "generate" is called result, and I'm trying to carry that across to my initial m file.
Any advice as to what I'm doing wrong would be greatly appreciated! Please and thank you
if your file generate.m defines a function it should have itself the following structure (which takes into account the fact that you have four returned values)
function [ret1 ret2 ret3 ret4] = generate(nrow,ncol,a,b)
.... % # Some processing of yours
ret1 = ... ; % # Returned values are eventually set
ret2 = ... ;
ret3 = ... ;
ret4 = ... ;
end
The function should be called (e.g. in your main script) as
[ret1 ret2 ret3 ret4] = generate(nrow,ncol,a,b);
now you have the variables ret1,ret2,ret3,ret4 available in the caller scope.
Be aware that the file generate.m must be in the current matlab PATH.

Resources